Boss 单挑战

Boss 单挑战

题面:

50s5vV.png

\(1\le N \le 10^3,1\le M\le 10^6,1\le HP,MP,SP\le 10^3\)

\(0\le N1,N2\le 10,1\le T\le 10\)


\(60\) 分就是暴力 dp。把各种状态都往里面塞暴力转移即可。我们考虑优化。

​ 考虑一个答案操作序列,我们将与喝血有关的操作归于 \(a\) 类,所有与魔法有关的操作归于 \(b\) 类,所有与愤怒值有关的操作归于 \(c\) 类。那么答案序列就是一个由 \(a,b,c\) 三类组成的序列。我们发现 \(b,c\) 类操作无论在哪一回合执行都一样,只要 \(b\) 类,\(c\) 类各自操作相对位置不变,那么效果是一样的。

​ 比如说,一个只包含 \(b,c\) 的操作序列是 \(b_1,c_1,c_2,c_3,b_2,c_4,b_3\) 。那么操作序列 \(b_1,b_2,b_3,c_1,c_2,c_3,c_4\) 与其是等效的。但是 \(b_2,b_1,b_3,c_1,c_2,c_3,c_4\) 与其却并不是等效的。因为 \(b\) 类的操作相对位置改变了。

​ 所以说,我们可以预处理出两个数组 \(mag[i][j]\)\(rage[i][j]\) 分别表示目前有 \(i\)\(mp/sp\),给 \(j\) 轮机会只用 \(mp/sp\) 能够打出的最大伤害。换成我们上面操作序列的说法就是。长为 \(j\)\(b/c\) 类序列能够造成的最大伤害。

​ 这个我们可以在 \(O(n^2\times(n1+n2))\) 的时间内出来。然后我们枚举两个操作序列的长度,得到最短的能够打出大于等于 \(m\) 的操作序列的长度 \(ans\)。那么我们接下来只要判定自己是不是可以靠喝药苟住 \(ans\) 轮。我们再跑一次 dp。设 \(life[i][j]\) 表示苟到第 \(i\) 轮的 boss 攻击结束后,还有 \(j\) 点血所需要喝的最小血瓶。然后我们枚举所需要喝的血瓶数,如果 \(life[i+ans-1][j](j\in[1,hp])\le i\) 更新最终答案 \(res=\min(res,ans+i)\) 。注意这里是第 \(i+ans-1\) 轮被打后还活着就行,因为第 \(i+ans\) 轮直接把他打死了,也就不会受到他的攻击了。

​ 然后考虑判断一定被打死和平局的情况。如果能在 \(n\) 轮内打死,那一定是有答案的,然后我们在判断一定被打死的情况。如果我们从 \(1\)\(n\) 轮一直只喝血药还会被打死,那就一定被打死。注意,存在我们一直只喝血药还会被打死但是仍旧能在 \(n\) 轮内打死 boss 的情况。比如 boss 在后面的轮数伤害很高,但是我们在他把我们打死之前就把他打死了。

​ 如果 \(n\) 轮一直喝血能不被打死的话,那就是平局了。

​ 那么总时间复杂度是 \(O(T\times(n^2\times(n1+n2)))\)

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e3+5;
int n,m,hp,mp,sp,dhp,dmp,dsp,x,a[MAXN];
int mag[MAXN][MAXN],rage[MAXN][MAXN],life[MAXN][MAXN];
int n1,n2,b[MAXN],y[MAXN],c[MAXN],z[MAXN];
int dfs_m(int now,int round)
{
	if(round==0) return 0;
	if(mag[now][round]!=-1) return mag[now][round];
	int ans=0;
	for(int i=1;i<=n1;++i)
		if(now>=b[i]) ans=max(ans,dfs_m(now-b[i],round-1)+y[i]);
	ans=max(ans,dfs_m(min(now+dmp,mp),round-1));
	return mag[now][round]=ans;	
}
int dfs_r(int now,int round)
{
	if(round==0) return 0;
	if(rage[now][round]!=-1) return rage[now][round];
	int ans=0;
	for(int i=1;i<=n2;++i)
		if(now>=c[i]) ans=max(ans,dfs_r(now-c[i],round-1)+z[i]);
	ans=max(ans,dfs_r(min(now+dsp,sp),round-1)+x);
	return rage[now][round]=ans;
}
int main()
{
	freopen("boss.in","r",stdin);
	freopen("boss.out","w",stdout);
	int T;scanf("%d",&T);
	while(T--)
	{
		memset(mag,-1,sizeof mag);memset(rage,-1,sizeof rage);memset(life,0x3f,sizeof life);
		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",&a[i]);
		scanf("%d",&n1);for(int i=1;i<=n1;++i) scanf("%d %d",&b[i],&y[i]);
		scanf("%d",&n2);for(int i=1;i<=n2;++i) scanf("%d %d",&c[i],&z[i]);
		for(int i=0;i<=n;++i)
		{
			dfs_m(mp,i);
			dfs_r(sp,i);
		}
		int ans=n+1;
		for(int i=0;i<=n;++i)
		{
			for(int j=0;i+j<=n;++j)
			{
				int dam=mag[mp][i]+rage[sp][j];
				if(dam>=m) ans=min(ans,i+j);
			}
		}
		bool death=0,win=0;
		int now=hp,more=n+1,cnt=0;
		for(int i=1;i<=n;++i)
		{
			now=min(hp,now+dhp);
			now-=a[i];
			if(now<=0) death=1;
		}
		if(ans<=n)
		{
			life[1][hp-a[1]]=0;life[0][hp]=0;
			for(int i=2;i<=n;++i)
			{
				for(int j=1;j<=hp;++j)
				{
					if(j+a[i]<=hp) life[i][j]=min(life[i-1][j+a[i]],life[i][j]);
					if(j+a[i]<=hp&&j+a[i]-dhp>=1) life[i][j]=min(life[i-1][j+a[i]-dhp]+1,life[i][j]);
				}
				for(int j=max(1,hp-dhp);j<=hp;++j)
					life[i][hp-a[i]]=min(life[i][hp-a[i]],life[i-1][j]+1);
			}
			for(int i=0;i+ans<=n;++i)
				for(int j=1;j<=hp;++j)
					if(life[ans-1+i][j]<=i) more=min(more,i);
		}
		if(ans+more<=n) printf("Yes %d\n",ans+more);
		else if(death) printf("No\n");
		else printf("Tie\n");
	}
	return 0;
}
posted @ 2021-10-20 09:20  夜空之星  阅读(29)  评论(0编辑  收藏  举报