#261 萌新拆塔 [状压DP][三进制]
题解
这道题真的很毒瘤啊(杜老师应该是只出毒瘤题的),当时看到这道题如此长的题面就直接挂机了[微笑];而且我还真的以为这道题是“10k模拟+玄学剪枝”,所以根本没有往DP那里去想……可能这就是菜鸡的最高境界吧……
如果没有模仿怪,那么这道题就应该是一个一维的二进制DP(不会存在什么时候吃宝石更优的问题),每一位表示这只怪兽是否被打,然后就可以切题了
然而有模仿怪啊!假设勇士打完一只怪兽之后吃到了一颗可以增加Inf攻击力和Inf防御力的宝石,而且后面只有模仿怪可以打,那勇士肯定就GG了啊。所以现在有三种情况要考虑:怪兽没打、怪兽被打但是它身后的宝石还没有被吃、怪兽被打而且它身后的宝石也被吃了。于是必须要用三进制状压(如果用二维DP+二进制状压的话会爆空间)。
设每一位有三种状态:
- 怪兽还没有被打
- 怪兽被打,但是它守护的宝石还没有被吃
- 怪兽被打,而且它守护的宝石已经被吃了
314 3 14 的空间不会爆
首先可以预处理每个状态下,勇士除了血量以外的所有量(毕竟这些东西只增不减),然后就可以开始愉快地DP了
如何愉快地DP ↓↓↓
a.如果某只怪兽处于1状态,判断这只怪兽是否能被打(判断 是否有必须先于它被打的怪兽 在它之后才被打);如果能打……按照要求和怪兽贴身搏斗就行[滑稽]
b.如果处于2状态,那么直接判断吃宝石是否更优
c.处于3状态的就可以不用管了
流程挺简单的,但是代码真的不好写啊(细节很多,我调试了半天)
代码
#include<iostream>
#include<cstdio>
#include<vector>
#define ll long long
using namespace std;
const int M=5000000,N=20;
int fa[M],fd[M],fm[M],a[N],d[N],s[N],Pow[N];ll fh[M],h[N];
int ap[N],dp[N],mp[N];ll hp[N];
vector<int>vec[N];
ll Fight(int Kni,int Mon){
ll H=(ll)fh[Kni],A=(ll)fa[Kni],D=(ll)fd[Kni],M=(ll)fm[Kni];
ll hh=(ll)h[Mon],aa=(ll)a[Mon],dd=(ll)d[Mon];
if(s[Mon]&8)aa=A,dd=D;if(s[Mon]&2)D=0;
if(A<=dd)return 0LL;if(aa<=D)return H;
aa-=D,A-=dd;if(s[Mon]&4)aa<<=1;
ll t=(ll)((hh-1)/A+(s[Mon]&1))*aa;//
if(t<=M)return H;return max(0LL,H-t+M);//
}
int main(){
int T;scanf("%d",&T);
Pow[0]=1;for(int i=1;i<=18;i++)Pow[i]=Pow[i-1]*3;
while(T--){
int A,D,M;ll H;scanf("%lld%d%d%d",&H,&A,&D,&M);
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)
vec[i].clear(),
scanf("%lld%d%d%d",&h[i],&a[i],&d[i],&s[i]),
scanf("%d%d%d%lld",&ap[i],&dp[i],&mp[i],&hp[i]);
int k;scanf("%d",&k);
for(int i=1,u,v;i<=k;i++)
scanf("%d%d",&u,&v),vec[v].push_back(u);
int Limit=Pow[n];
for(int Mask=0;Mask<Limit;Mask++){
fh[Mask]=0,fa[Mask]=A,fd[Mask]=D,fm[Mask]=M;
for(int i=1;i<=n;i++)
if(Mask/Pow[i-1]%3==2)
fa[Mask]+=ap[i],fd[Mask]+=dp[i],fm[Mask]+=mp[i];
}
fh[0]=H;
for(int Mask=0;Mask<Limit;Mask++){
if(!fh[Mask])continue;//
for(int i=1;i<=n;i++){
if(Mask/Pow[i-1]%3==0){
bool ok=true;
for(int j=vec[i].size()-1;j>=0;j--)
if(Mask/Pow[vec[i][j]-1]%3==0){ok=false;break;}
if(!ok)continue;
ll Rest=Fight(Mask,i);
fh[Mask+Pow[i-1]]=max(fh[Mask+Pow[i-1]],Rest);
}else if(Mask/Pow[i-1]%3==1)
fh[Mask+Pow[i-1]]=max(fh[Mask+Pow[i-1]],fh[Mask]+hp[i]);
}
}
printf("%lld\n",fh[Limit-1]?fh[Limit-1]:-1);
}
return 0;
}