bzoj2964 Boss单挑战

13 篇文章 0 订阅

Description

某RPG游戏中,最后一战是主角单挑Boss,将其简化后如下:
  主角的气血值上限为HP,魔法值上限为MP,愤怒值上限为SP;Boss仅有气血值,其上限为M。
  现在共有N回合,每回合都是主角先行动,主角可做如下选择之一:
  1. 普通攻击:减少对方X的气血值,并增加自身DSP的愤怒值。(不超过上限)
  2. 法术攻击:共有N1种法术,第i种消耗Bi的魔法值,减少对方Yi的气血值。(使用时要保证MP不小于Bi)
  3. 特技攻击:共有N2种特技,第i种消耗Ci的愤怒值,减少对方Zi的气血值。(使用时要保证SP不小于Ci)
  4. 使用HP药水:增加自身DHP的气血值。(不超过上限)
  5. 使用MP药水:增加自身DMP的魔法值。(不超过上限)
  之后Boss会攻击主角,在第i回合减少主角Ai的气血值。
  刚开始时气血值,魔法值,愤怒值都是满的。当气血值小于等于0时死亡。
  如果主角能在这N个回合内杀死Boss,那么先输出“Yes”,之后在同一行输出最早能在第几回合杀死Boss。(用一个空格隔开)
  如果主角一定会被Boss杀死,那么输出“No”。
  其它情况,输出“Tie”。

Input

输入的第一行包含一个整数T,为测试数据组数。
接下来T部分,每部分按如下规则输入:
第一行九个整数N, M, HP, MP, SP, DHP, DMP, DSP, X。
第二行N个整数Ai。
第三行第一个整数N1,接下来包含N1对整数Bi, Yi。
第四行第一个整数N2,接下来包含N2对整数Ci, Zi。

Output

输出共包含T行,每行依次对应输出一个答案。

Sample Input

2
5 100 100 100 100 50 50 50 20
50 50 30 30 30
1 100 40
1 100 40
5 100 100 100 100 50 50 50 10
50 50 30 30 30
1 100 40
1 100 40

Sample Output

Yes 4
Tie

样例说明

对于第一个样例,主角的策略是:第一回合法术攻击,第二回合使用HP药水,第三回合特技攻击,第四回合普通攻击。

HINT

对于100%的数据:1 ≤ N ≤ 1000,1 ≤ M ≤ 1000000,1 ≤ HP,MP,SP ≤ 1000,N1,N2 ≤ 10,DHP,Ai ≤ HP,DMP,Bi ≤ MP,DSP,Ci ≤ SP,X,Yi,Zi ≤ 10000,1 ≤ T ≤ 10。

Source

中国国家队清华集训 2012-2013 第三天

一眼背包无疑,这是我做过的最长最难的背包dp了。这道题需要考虑三个东西,魔法、怒气和生命值。
如果直接将三个东西放在一起转移,想必让我推一天也推不出来方程,毕竟状态实在太多了。
那么我们可以换个思路,将三个东西分开处理。
先赋代码,再讲解:

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int a[1001],dpmp[1001],dpsp[1001],dphp[1001][1001];
int gmp[1001][1001],gsp[1001][1001];
int b[11],y[11],c[11],z[11];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void solve()
{
    memset(gmp,0,sizeof(gmp));
    memset(gsp,0,sizeof(gsp));
    memset(dpmp,0,sizeof(dpmp));
    memset(dpsp,0,sizeof(dpsp));
    memset(dphp,-INF,sizeof(dphp));
    int n,m,hp,mp,sp,dhp,dmp,dsp,x;
    n=read();m=read();hp=read();mp=read();sp=read();
    dhp=read();dmp=read();dsp=read();x=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    int n1=read();
    for(int i=1;i<=n1;i++)
    {
        b[i]=read();y[i]=read();
    }
    int n2=read();
    for(int i=1;i<=n2;i++)
    {
        c[i]=read();z[i]=read();
    }
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=mp;j++)
            dpmp[i]=max(dpmp[i],gmp[i][j]);
        if(i<n)
            for(int j=0;j<=mp;j++)
            {
                gmp[i+1][min(mp,j+dmp)]=max(gmp[i+1][min(mp,j+dmp)],gmp[i][j]);
                for(int k=1;k<=n1;k++)
                    if(j-b[k]>=0)
                        gmp[i+1][j-b[k]]=max(gmp[i+1][j-b[k]],gmp[i][j]+y[k]/**/);
            }
    }
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=sp;j++)
            dpsp[i]=max(dpsp[i],gsp[i][j]);
        if(i<n)
            for(int j=0;j<=sp;j++)
            {
                gsp[i+1][min(sp,j+dsp)]=max(gsp[i+1][min(sp,j+dsp)],gsp[i][j]+x);
                for(int k=1;k<=n2;k++)
                    if(j-c[k]>=0)
                        gsp[i+1][j-c[k]]=max(gsp[i+1][j-c[k]],gsp[i][j]+z[k]/**/);
            }
    }
    int minx=INF;
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++)
            if(dpmp[i]+dpsp[j]>=m)
                minx=min(minx,j+i);
    dphp[1][hp]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=hp;j++)
            if(dphp[i][j]>=minx)
            {
                printf("Yes %d\n",i);
                return ;
            }
        for(int j=1;j<=hp;j++)
        {
            if(min(j+dhp,hp)>a[i])
                dphp[i+1][min(j+dhp,hp)-a[i]]=max(dphp[i+1][min(j+dhp,hp)-a[i]],dphp[i][j]);
            if(j>a[i])
                dphp[i+1][j-a[i]]=max(dphp[i+1][j-a[i]],dphp[i][j]+1);
        }
    }
    for(int i=1;i<=hp;i++)
        if(dphp[n+1][i]>=0)
        {
            printf("Tie\n");
            return ;
        }
    printf("No\n");
    return ;
}
int main()
{
    int T=read();
    while(T--)
        solve();
    return 0;
}

我直接说 solve 里面的东西了。
gmp[i][j] 表示不管 hp 和 sp,进行到第 i 个回合时还剩 j mp 时最大的魔法攻击值,同理 gsp[i][j] 表示不管 hp 和 mp,进行到第 i 个回合时还剩 j sp 时最大的怒气攻击值。
dpmp 和 dpsp 就直接表示进行到第 i 回合时的最大攻击值。

for(int i=0;i<=n;i++)
    {
        for(int j=0;j<=mp;j++)
            dpmp[i]=max(dpmp[i],gmp[i][j]);//将g数组赋回dp数组
        if(i<n)
            for(int j=0;j<=mp;j++)
            {
                gmp[i+1][min(mp,j+dmp)]=max(gmp[i+1][min(mp,j+dmp)],gmp[i][j]);//此处嗑蓝瓶
                for(int k=1;k<=n1;k++)
                    if(j-b[k]>=0)
                        gmp[i+1][j-b[k]]=max(gmp[i+1][j-b[k]],gmp[i][j]+y[k]/**/);//进行攻击
            }
    }

sp 的方程一样,不再赘述。

然后的代码是这样:

int minx=INF;
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++)
            if(dpmp[i]+dpsp[j]>=m)
                minx=min(minx,j+i);
    dphp[1][hp]=1;

这里的意思是算出将Boss打死需要的最小回合数。算出 minx 之后只需算 hp,在 hp>0 的情况下的最小大于minx的回合数就行。
dphp[i][j] 表示进行到第 i 回合,还剩 j hp 时可以攻击的最大次数。
算 hp:

for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=hp;j++)
            if(dphp[i][j]>=minx)//如果攻击次数大于minx,就Yes
            {
                printf("Yes %d\n",i);
                return ;
            }
        for(int j=1;j<=hp;j++)
        {
            if(min(j+dhp,hp)>a[i])
                dphp[i+1][min(j+dhp,hp)-a[i]]=max(dphp[i+1][min(j+dhp,hp)-a[i]],dphp[i][j]);//嗑红瓶
            if(j>a[i])
                dphp[i+1][j-a[i]]=max(dphp[i+1][j-a[i]],dphp[i][j]+1/*可以攻击就+1*/);
        }
    }

最后如果没有输出Yes,那么先这样:

for(int i=1;i<=hp;i++)
        if(dphp[n+1][i]>=0)
        {
            printf("Tie\n");
            return ;
        }

也就是说,如果双方都打不死对方,就输出Tie。
无法输出Tie,就输出No。

printf("No\n");
    return ;

多好的一题啊。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值