#261 萌新拆塔 [状压DP][三进制]

#261 萌新拆塔 [状压DP][三进制]

题目传送门

题解

这道题真的很毒瘤啊(杜老师应该是只出毒瘤题的),当时看到这道题如此长的题面就直接挂机了[微笑];而且我还真的以为这道题是“10k模拟+玄学剪枝”,所以根本没有往DP那里去想……可能这就是菜鸡的最高境界吧……

如果没有模仿怪,那么这道题就应该是一个一维的二进制DP(不会存在什么时候吃宝石更优的问题),每一位表示这只怪兽是否被打,然后就可以切题了

然而有模仿怪啊!假设勇士打完一只怪兽之后吃到了一颗可以增加Inf攻击力和Inf防御力的宝石,而且后面只有模仿怪可以打,那勇士肯定就GG了啊。所以现在有三种情况要考虑:怪兽没打、怪兽被打但是它身后的宝石还没有被吃、怪兽被打而且它身后的宝石也被吃了。于是必须要用三进制状压(如果用二维DP+二进制状压的话会爆空间)。

设每一位有三种状态:

  1. 怪兽还没有被打
  2. 怪兽被打,但是它守护的宝石还没有被吃
  3. 怪兽被打,而且它守护的宝石已经被吃了

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值