bzoj 2964 boss单挑战 个人心得

明天还要期末考试今天还在浪。。。
题目链接:bzoj 2964
说来惭愧这题的思路是从网上找的题解看的,本来觉着很好写,真正写起来才发现有不少需要注意的地方;
主体思路是将法攻和怒功分开dp,本来以为关于恢复生命的技能的使用次数做个加减法就好了,事实上并没有这么简单,只好再进行一次dp;
由于本人太弱,实在想不起如何从i-1向i转移,只能从i向i+1进行向后转移,好像这样也易于理解?
其中一定要牢记技能攻击的顺序,是先加血再受伤!!!
代码:

#include<cstdio>
#include<algorithm>
const int maxn=1005;
using std::max;
using std::min;
int t,n,m,hp,mp,sp,dhp,dmp,dsp,x;
int DP[maxn][maxn];
struct asd
{
    int xh,ct;
};
asd b[11];
asd c[11];
void dtgh(int N,int sh,int hf,int maxp,asd *a,int num,int *ans)
{
    for(int i=0;i<=N;i++)
    {
        ans[i]=0;
        for(int j=0;j<=maxp;j++)
            DP[i][j]=0;
    }
    for(int i=0;i<N;i++)
    {
        for(int j=0;j<=maxp;j++)
        {
            for(int k=0;k<=num;k++)
                if(k==0)
                {
                    int tmp=j+hf;
                    if(tmp>maxp)tmp=maxp;
                    DP[i+1][tmp]=max(DP[i][j]+sh,DP[i+1][tmp]);
                }
                else if(j>=a[k].xh)
                    DP[i+1][j-a[k].xh]=max(DP[i+1][j-a[k].xh],DP[i][j]+a[k].ct);
            ans[i]=max(ans[i],DP[i][j]);
        }
    }
    for(int i=0;i<=maxp;i++)ans[N]=max(ans[N],DP[N][i]);
}
int ai[maxn];
int ans1[maxn],ans2[maxn];
int bld[maxn][maxn];
int tim[maxn];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d%d%d%d%d%d%d",&n,&m,&hp,&mp,&sp,&dhp,&dmp,&dsp,&x);
        for(int i=1;i<=n;i++)scanf("%d",&ai[i]);
        int tmp;
        scanf("%d",&tmp);
        for(int i=1;i<=tmp;i++)scanf("%d%d",&b[i].xh,&b[i].ct);
        dtgh(n,0,dmp,mp,b,tmp,ans1);
        scanf("%d",&tmp);
        for(int i=1;i<=tmp;i++)scanf("%d%d",&c[i].xh,&c[i].ct);     
        dtgh(n,x,dsp,sp,c,tmp,ans2);
        int flag=0;
        int rhp=hp;
        tim[0]=0;
        for(int i=0;i<=n;i++)tim[i]=n+1;
        for(int i=0;i<=n;i++)
        for(int j=0;j<=hp;j++)bld[i][j]=n+1;
        bld[0][hp]=0;
        for(int i=0;i<n;i++)
        {
            for(int j=1;j<=hp;j++)
            {
                int tmp=j-ai[i+1];
                if(tmp>0)
                bld[i+1][tmp]=min(bld[i+1][tmp],bld[i][j]);
                if(j!=hp)
                {
                    tmp=j+dhp;
                    if(tmp>hp)tmp=hp;
                    tmp-=ai[i+1];//注意如果生命值满了需要先加生命再受伤,这地方坑了我好久;
                    if(tmp>0)
                    bld[i+1][tmp]=min(bld[i+1][tmp],bld[i][j]+1);
                }
                tim[i]=min(tim[i],bld[i][j]);
            }
        }
        for(int i=1;i<=hp;i++)tim[n]=min(tim[n],bld[n][i]);
        for(int i=1;i<=n;i++)
        {   
            int tmpsh=0;
            for(int k=0;k<=i-tim[i-1];k++)
                tmpsh=max(ans1[k]+ans2[i-tim[i-1]-k],tmpsh);
            if(m<=tmpsh)
            {
                printf("Yes %d\n",i);
                flag=1;
                break;
            }
            if(tim[i]>i)
            {
                printf("No\n");
                flag=1;
                break;
            }       
        }
        if(!flag)printf("Tie\n");   
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值