补题日记[2022杭电暑期多校3]B-Boss Rush
Posted cls1277
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了补题日记[2022杭电暑期多校3]B-Boss Rush相关的知识,希望对你有一定的参考价值。
Pro
https://acm.hdu.edu.cn/showproblem.php?pid=7163
Sol
这个题最后也没有A掉。主要是因为太懒了因为当时看了思路之后自己写了一下自己以为的正解,还调了好几个小时才发现自己的写法和std的区别在哪,因此没有再对照着std写一遍,而是将那部分时间用来研究思路以及为什么错。
这个题的题意我感觉不太好理解,我也是边写边调才慢慢理解了出题人的想法 ,但是单调性还是很容易看出来,因此本题就是二分答案。二分在x帧内能打到的最高伤害,然后与H比较是否满足条件。
问题在于二分的judge(check)函数怎么写:正确的做法为:因为最多只有18个技能,因此考虑类似状压dp的方法,用 S S S十进制数表示状态, f [ S ] f[S] f[S]表示该状态下能打出的最高伤害,因此当枚举 S S S时,可以通过判断技能是否在该状态进行 O ( 1 ) O(1) O(1)的转移,具体实现方法看下面的std。因此通过状态的累计计算就可以得到T帧下能否打出H的伤害,也就可以判断T帧是否满足条件。
而一种错误的写法为:不进行状态转移,对于枚举的每一个状态 S S S都重新计算能够打出的最高伤害,从而判断能否打出H。但重新计算的方法不对,并不能通过很快的时间内算出对于状态 S S S中为1的下标对应的技能是否需要全被打完。而状压dp的写法可以避免这种问题,通过状态的积累,来到当前状态的即为最优状态,只需要考虑当前这个技能是否全都打完即可。
下面是优雅的std。
Code
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=18,M=100005;
int Case,n,i,j,S,t[N],d[N],l,r,ans,mid,sum[(1<<N)+1];
ll hp,f[(1<<N)+1],dmg[N][M];
inline void up(ll&a,ll b)a<b?(a=b):0;
bool check(int T)
int S,i;
for(S=0;S<1<<n;S++)f[S]=-1;
f[0]=0;
for(S=0;S<1<<n;S++)
ll w=f[S];
if(w<0)continue;
if(w>=hp)
#ifdef DEBUG
cout<<T<<' '<<S<<endl;
#endif
return 1;
int cur=sum[S];
if(cur>T)continue;
for(i=0;i<n;i++)if(!(S>>i&1))
if(cur+d[i]-1<=T)up(f[S|(1<<i)],w+dmg[i][d[i]-1]);
else up(f[S|(1<<i)],w+dmg[i][T-cur]);
return 0;
int main()
#ifdef DEBUG
freopen("data.txt","r",stdin);
#endif
scanf("%d",&Case);
while(Case--)
scanf("%d%lld",&n,&hp);
ans=-1,l=r=0;
for(i=0;i<n;i++)
scanf("%d%d",&t[i],&d[i]);
r+=t[i]+d[i]-1;
for(j=0;j<d[i];j++)scanf("%lld",&dmg[i][j]);
for(j=1;j<d[i];j++)dmg[i][j]+=dmg[i][j-1];
for(S=1;S<1<<n;S++)sum[S]=sum[S-(S&-S)]+t[__builtin_ctz(S&-S)];
while(l<=r)
mid=(l+r)>>1;
if(check(mid))r=(ans=mid)-1;else l=mid+1;
printf("%d\\n",ans);
以上是关于补题日记[2022杭电暑期多校3]B-Boss Rush的主要内容,如果未能解决你的问题,请参考以下文章