[关闭]
@2368860385 2020-11-07T03:02:08.000000Z 字数 2345 阅读 158

后:day6

衢州


预计40 + 30 = 70
实际40 + 30 = 70

T1

只想到搜索+剪枝,赛后一看没有模仿怪的,就明白了加dp记录当前状态的血量最大值就好了,当前状态的其他属性不变(攻击力,防御力,魔防这些值对于一个状态时固定的,把打过的怪的宝石全吃了)。
但是有模仿怪的时候,先吃宝石不一定最优,于是可以在记录当前那些宝石没吃。发现空间为,时间也过不去。挖掘性质:发现没吃的宝石一定是打完怪的子集,就是只有当一个怪打过后,这个宝石才可能吃或不吃。一个怪和它的宝石只有三种状态:怪没打,怪打了而宝石没吃,怪打了宝石也吃了。将状态合并,就是一个三进制数:
第 i 位是 0 表示第 i 个怪物还没打。
第 i 位是 1 表示第 i 个怪物被打败了,但是它守护的宝石还没吃。
第 i 位是 2 表示第 i 个怪物被打败了,它守护的宝石已经吃掉了。

回过头来看看我们优化了那种状态。
00 怪没打,宝石没吃
01 怪没打,宝石吃了???不合法的状态。
10 怪打了,宝石没吃
11 怪打了,宝石吃了
所以我们优化一个状态。

代码

  1. // s 是一个三进制数。
  2. // 第 i 位是 0 表示第 i 个怪物还没打。
  3. // 第 i 位是 1 表示第 i 个怪物被打败了,但是它守护的宝石还没吃。
  4. // 第 i 位是 2 表示第 i 个怪物被打败了,它守护的宝石已经吃掉了。
  5. #include<cstdio>
  6. #include<algorithm>
  7. #include<cstring>
  8. #include<cmath>
  9. #include<iostream>
  10. #include<cctype>
  11. #include<set>
  12. #include<vector>
  13. #include<queue>
  14. #include<map>
  15. using namespace std;
  16. typedef long long LL;
  17. inline int read() {
  18. int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
  19. for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
  20. }
  21. #define rep(a,b,c) for (int a = (b); a <= (c); ++a)
  22. const int N = 5000100; // 3^14
  23. struct G{
  24. int h,a,d,s,hp,ap,dp,mp;
  25. }g[20];
  26. int mi[20], fa[N], fd[N], fm[N];
  27. LL dp[N];
  28. vector<int>pre[20];
  29. int H, A, D, M, n;
  30. void init() {
  31. H = read(), A = read(), D = read(), M = read(), n = read();
  32. rep (i, 1, n) {
  33. g[i].h = read(), g[i].a = read(), g[i].d = read(), g[i].s = read();
  34. g[i].ap = read(), g[i].dp = read(), g[i].mp = read(), g[i].hp = read();
  35. }
  36. rep (i, 1, n) pre[i].clear();
  37. int k = read();
  38. rep (i, 1, k) {
  39. int u = read(), v = read();
  40. pre[v].push_back(u);
  41. }
  42. }
  43. LL zhan(int i,int j) {
  44. LL H = dp[i];int A = fa[i], D = fd[i], M = fm[i];
  45. int h = g[j].h, a = g[j].a, d = g[j].d, s = g[j].s; //---
  46. if (s & 8) a = A, d = D; // 模仿
  47. if (s & 2) D = 0; // 无视防御
  48. int AA = max(0, A - d); // 勇士造成伤害
  49. int aa = max(0, a - D) * (((s >> 2) & 1) + 1); // 怪物造成的攻击力,是否连击
  50. if (AA == 0) return 0;
  51. LL t1 = (h - 1) / AA + 1; // 需要打怪几次
  52. LL t2 = (s & 1) ? (t1 * aa) : ((t1 - 1) * aa); // 怪造成的攻击力,是否有先攻
  53. LL t3 = max(0ll, t2 - M); // 减去魔防
  54. return max(0ll, H - t3);
  55. }
  56. void solve() {
  57. int m = mi[n] - 1;
  58. memset(dp, 0, sizeof(dp));
  59. rep (s, 0, m) {
  60. fa[s] = A, fd[s] = D, fm[s] = M;
  61. rep (i, 1, n)
  62. if (s / mi[i - 1] % 3 == 2)
  63. fa[s] += g[i].ap, fd[s] += g[i].dp, fm[s] += g[i].mp;
  64. // 每个状态下的攻击力,防御力,以及魔防
  65. }
  66. dp[0] = H; //---
  67. rep (s, 0, m) {
  68. if (!dp[s]) continue;
  69. rep (i, 1, n) {
  70. if (s / mi[i - 1] % 3 == 0) { // 找到一个没打的怪
  71. bool flag = true; // 是否能打
  72. int t = pre[i].size() - 1;
  73. rep (j, 0, (t))
  74. if (s / mi[pre[i][j] - 1] % 3 == 0) { flag = false; break; }
  75. if (!flag) continue;
  76. LL nh = zhan(s, i); // 战斗后的hp
  77. if (nh > 0) dp[s + mi[i - 1]] = max(dp[s + mi[i - 1]], nh); // 这一位变成1,但没有吃宝石
  78. }
  79. else if (s / mi[i - 1] % 3 == 1) {
  80. dp[s + mi[i - 1]] = max(dp[s + mi[i - 1]], dp[s] + g[i].hp); // 吃宝石
  81. }
  82. }
  83. }
  84. LL Ans = dp[m] == 0 ? -1 : dp[m];
  85. cout << Ans << "\n";
  86. }
  87. int main() {
  88. mi[0] = 1;
  89. for (int i=1; i<=15; ++i) mi[i] = mi[i - 1] * 3;
  90. int Case = read();
  91. while (Case --) {
  92. init();
  93. solve();
  94. }
  95. return 0;
  96. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注