洛谷P3724 大佬 [AH2017/HNOI2017] dp+bfs

正解:dp+bfs

解题报告:

传送门!

这题看起来很复杂的样子其实真的很复杂

但是仔细看一下题目,会发现其实操作只有两个目的嘛,一个是保证自己不死,一个是让对手减血

而且保证自己不死只有一种操作

而且这种操作和其他东西都麻油关系,不管多少血量,不管你前面的操作是哪些,反正加的血量是一样的

所以考虑把这两个分开来思考

首先求血量

考虑到要留尽量多的时间让对手减血,而对于攻击对手这个事儿来说,无论哪天攻击造成的伤害都是相等的,对于具体日期麻油要求,看到这样子就要想到dp了嘛(具体日期无影响鸭QAQ

所以设状态f[i][j]:第i天血量为j,最多不用刷题(也就是涨血)几天

转移很简单嘛,普通背包

然后取dp中的max,也就是最多能用来攻击对手的时间D

接下来考虑攻击对手

首先肯定是要么345组合拳要么1,相当于只有两种

设用345的两次方案分别是(d1,h1)(d2,h2),表示积累d天伤害为h

再设对手的血量为HP

然后如果要战胜对手,首先不能血量为负,所以h1+h2<=HP

又要活下去,所以HP-h1-h2<=D-d1-d2

然后上面是攻击两次嘛,如果攻击一次相当于有一组=(0,0)就好,不攻击就是两组都=(0,0)就好

然后就做完了

然后说下具体做法

首先就枚举状态昂,bfs枚举出所有可能的状态,然后把这些状态按h为第一关键字d为第二关键字地排序

然后对h从大到小地枚举第一次攻击

第二次攻击,可以先对上面的式子移项,得D>=HP-h1-h2+d1+d2

又h1d1其实是已知式,所以就是D>=T+d2-h2

如果有合法方案就说明对手会死嘛,所以要努力让它合法,就是说要努力保证d2-h2合法

但还有一个要求昂,就h1+h2<=HP

所以就在满足h1+h2<=HP的条件下,增大h2(即指针向后扫),每次取(-h2+d2)min,然后比较,O(状态数)扫过去就好

然后另外有个小细节就是这里有个手写hash

但是很简单,简单的hash思想+链式前向星思想就好,不想多说,自己看代码就好QAQ

 

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define rg register
#define gc getchar()
#define mp make_pair
#define fr first
#define sc second
#define int long long
#define rp(i,x,y) for(rg int i=x;i<=y;++i)
#define my(i,x,y) for(rg int i=x;i>=y;--i)
#define e(i,x) for(rg int i=head[x];i;i=edge[i].nxt)

const int N=200+10,M=2000000+10000,mod=10162003;
int n,m,mc,a[N],w[N],c[N],f[N][N],dmx,mxc,zt_cnt;
pair<int,int>zt[M];
struct node{int f,l,d;};
struct hsh
{
    struct ed{int x,y,nxt;}edge[M];
    int head[mod+100],cnt;
    void push(int x,int y){int pos=(1ll*x*101+y)%mod;edge[++cnt]=(ed){x,y,head[pos]};head[pos]=cnt;}
    bool query(int x,int y){int pos=(1ll*x*101+y)%mod;e(i,pos)if(edge[i].x==x&&edge[i].y==y)return true;return false;}
}mapp;


il int read()
{
    char ch=gc;int x=0;bool y=1;
    while(ch!='-' && (ch>'9' || ch<'0'))ch=gc;
    if(ch=='-')ch=gc,y=0;
    while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc;
    return y?x:-x;
}
il void dp()
{
    rp(i,1,n)
        rp(j,a[i],mc)
        {
            f[i][j-a[i]]=max(f[i-1][j]+1,f[i][j-a[i]]);
            f[i][min(j-a[i]+w[i],mc)]=max(f[i-1][j],f[i][min(j-a[i]+w[i],mc)]);
        }
    rp(i,1,n)rp(j,0,mc)dmx=max(dmx,f[i][j]);
}
il void bfs()
{
    queue<node>Q;Q.push((node){1,0,1});
    while(!Q.empty())
    {
        node tmp=Q.front();Q.pop();
        if(tmp.d==dmx)continue;
        Q.push((node){tmp.f,tmp.l+1,tmp.d+1});
        if(tmp.l>1 && 1ll*tmp.f*tmp.l<=mxc && !mapp.query(tmp.f*tmp.l,tmp.d+1))
        {
            Q.push((node){1ll*tmp.f*tmp.l,tmp.l,tmp.d+1});
            zt[++zt_cnt]=mp(1ll*tmp.f*tmp.l,tmp.d+1);
            mapp.push(tmp.f*tmp.l,tmp.d+1);
        }
    }
}
il void wk(int hp)
{
    if(hp<=dmx)return void(printf("1\n"));
    int mn=1e9;int k=1;
    my(j,zt_cnt,1)
    {
            while(k<zt_cnt && zt[k].fr+zt[j].fr<=hp)mn=min(mn,zt[k].sc-zt[k].fr),++k;
            if(mn+hp-zt[j].fr+zt[j].sc<=dmx)return void(printf("1\n"));
            if(zt[j].fr<=hp && hp-zt[j].fr<=dmx-zt[j].sc)return void(printf("1\n"));
    }
    printf("0\n");
}

main()
{
//      freopen("dl.in","r",stdin);freopen("dl.out","w",stdout);
    n=read();m=read();mc=read();rp(i,1,n)a[i]=read();rp(i,1,n)w[i]=read();rp(i,1,m)mxc=max(mxc,c[i]=read());
    dp();bfs();sort(zt+1,zt+1+zt_cnt);rp(i,1,m)wk(c[i]);
    return 0;
}
这儿是代码QAQ

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值