@2368860385
2020-11-07T03:02:08.000000Z
字数 2345
阅读 158
衢州
预计40 + 30 = 70
实际40 + 30 = 70
只想到搜索+剪枝,赛后一看没有模仿怪的,就明白了加dp记录当前状态的血量最大值就好了,当前状态的其他属性不变(攻击力,防御力,魔防这些值对于一个状态时固定的,把打过的怪的宝石全吃了)。
但是有模仿怪的时候,先吃宝石不一定最优,于是可以在记录当前那些宝石没吃。发现空间为,时间也过不去。挖掘性质:发现没吃的宝石一定是打完怪的子集,就是只有当一个怪打过后,这个宝石才可能吃或不吃。一个怪和它的宝石只有三种状态:怪没打,怪打了而宝石没吃,怪打了宝石也吃了。将状态合并,就是一个三进制数:
第 i 位是 0 表示第 i 个怪物还没打。
第 i 位是 1 表示第 i 个怪物被打败了,但是它守护的宝石还没吃。
第 i 位是 2 表示第 i 个怪物被打败了,它守护的宝石已经吃掉了。
回过头来看看我们优化了那种状态。
00 怪没打,宝石没吃
01 怪没打,宝石吃了???不合法的状态。
10 怪打了,宝石没吃
11 怪打了,宝石吃了
所以我们优化一个状态。
代码
// s 是一个三进制数。
// 第 i 位是 0 表示第 i 个怪物还没打。
// 第 i 位是 1 表示第 i 个怪物被打败了,但是它守护的宝石还没吃。
// 第 i 位是 2 表示第 i 个怪物被打败了,它守护的宝石已经吃掉了。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<vector>
#include<queue>
#include<map>
using namespace std;
typedef long long LL;
inline int read() {
int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}
#define rep(a,b,c) for (int a = (b); a <= (c); ++a)
const int N = 5000100; // 3^14
struct G{
int h,a,d,s,hp,ap,dp,mp;
}g[20];
int mi[20], fa[N], fd[N], fm[N];
LL dp[N];
vector<int>pre[20];
int H, A, D, M, n;
void init() {
H = read(), A = read(), D = read(), M = read(), n = read();
rep (i, 1, n) {
g[i].h = read(), g[i].a = read(), g[i].d = read(), g[i].s = read();
g[i].ap = read(), g[i].dp = read(), g[i].mp = read(), g[i].hp = read();
}
rep (i, 1, n) pre[i].clear();
int k = read();
rep (i, 1, k) {
int u = read(), v = read();
pre[v].push_back(u);
}
}
LL zhan(int i,int j) {
LL H = dp[i];int A = fa[i], D = fd[i], M = fm[i];
int h = g[j].h, a = g[j].a, d = g[j].d, s = g[j].s; //---
if (s & 8) a = A, d = D; // 模仿
if (s & 2) D = 0; // 无视防御
int AA = max(0, A - d); // 勇士造成伤害
int aa = max(0, a - D) * (((s >> 2) & 1) + 1); // 怪物造成的攻击力,是否连击
if (AA == 0) return 0;
LL t1 = (h - 1) / AA + 1; // 需要打怪几次
LL t2 = (s & 1) ? (t1 * aa) : ((t1 - 1) * aa); // 怪造成的攻击力,是否有先攻
LL t3 = max(0ll, t2 - M); // 减去魔防
return max(0ll, H - t3);
}
void solve() {
int m = mi[n] - 1;
memset(dp, 0, sizeof(dp));
rep (s, 0, m) {
fa[s] = A, fd[s] = D, fm[s] = M;
rep (i, 1, n)
if (s / mi[i - 1] % 3 == 2)
fa[s] += g[i].ap, fd[s] += g[i].dp, fm[s] += g[i].mp;
// 每个状态下的攻击力,防御力,以及魔防
}
dp[0] = H; //---
rep (s, 0, m) {
if (!dp[s]) continue;
rep (i, 1, n) {
if (s / mi[i - 1] % 3 == 0) { // 找到一个没打的怪
bool flag = true; // 是否能打
int t = pre[i].size() - 1;
rep (j, 0, (t))
if (s / mi[pre[i][j] - 1] % 3 == 0) { flag = false; break; }
if (!flag) continue;
LL nh = zhan(s, i); // 战斗后的hp
if (nh > 0) dp[s + mi[i - 1]] = max(dp[s + mi[i - 1]], nh); // 这一位变成1,但没有吃宝石
}
else if (s / mi[i - 1] % 3 == 1) {
dp[s + mi[i - 1]] = max(dp[s + mi[i - 1]], dp[s] + g[i].hp); // 吃宝石
}
}
}
LL Ans = dp[m] == 0 ? -1 : dp[m];
cout << Ans << "\n";
}
int main() {
mi[0] = 1;
for (int i=1; i<=15; ++i) mi[i] = mi[i - 1] * 3;
int Case = read();
while (Case --) {
init();
solve();
}
return 0;
}