2018 ICPC EC-final i题Misunderstood Missing

题意:A代表你的初始攻击力,D代表你每回合递增攻击力,A,D初始为0。

第一行给一个t,代表t组测试数据

给你N个回合(n<=100),第i个回合有3个数字a[i],b[i],c[i](范围均1e9),代表有三种选择,1.攻击,造成A+a[i]的伤害

2.使D增加b[i]

3.使A增加c[i]

问,经过N回合后最多可以造成多少伤害

 

样例如下。。


2018 ICPC EC-final i题Misunderstood Missing_第1张图片

 

 

一眼看出是个DP,但是不好推。一开始在想DP存什么,怎么开数组,因为当前造成的伤害,A,D的数量级都是1e9.又不能贪心证明当前的三种选择哪一种最优。想了一个小时快自闭才发现倒推。。实属DD。

想出倒推之后转化题意,倒推的过程中是不知道现在的A和D是多少的。那么我只用管造成的伤害就行了。设当前回合为第i回合。如果选择攻击,那么造成伤害+a[i],假设当前回合选择使A增加c[i],那么在第i-n的回合中每一次进攻造成的伤害就会增加c[i],倒推过程中,假设之前进攻了k次,并且当前回合选择了把A增加c[i],那么伤害就会增加k*c[i]。同理,如果当前回合选择使D增加b[i],那么如果在第q个回合攻击,伤害就会增加(q-i)*b[i],这是进攻一次的情况。那么如果这个回合之后进攻了k次,那么每次伤害会增加(q1+q2+...+qk  - k*i )*b[i],令之前那些q的总和为j,那么伤害就会增加(j-i*k)*b[i]。这里的j指“倒推过程中,之前进攻的回合数之和”。

 

#include 
using namespace std;
#define ll long long
#define last (i+1)%2
#define now i%2
ll dp[2][105][5051];//第一维代表当前回合,由于害怕空间不够,用滚动数组和上面define对应,
//第二维代表之前进攻次数。5051为进攻回合数之和(从1加到100最多5050)
ll a[105],b[105],c[105],n,t;
int main(){
	cin>>t;
	while(t--){
		cin>>n;
		ll ans=0;
		memset(dp,0,sizeof dp);
		for(int i=1;i<=n;i++)
			cin>>a[i]>>b[i]>>c[i];
		dp[n%2][1][n]=a[n];//第n回合肯定会进攻,进攻次数=1,进攻回合总和=n,伤害为a[n]
		for(int i=n-1;i>=1;i--){//倒推枚举回合数 
			for(int k=n-i;k>=1;k--){//倒推枚举攻击次数 
				ll minn=(i+i+k-1)*k/2,maxx=(n+n-k+1)*k/2;//枚举上界和下界,从1到5050应该也行,相当于剪枝
				for(int j=maxx;j>=minn;j--){//倒推枚举回合数之和
					dp[now][k+1][j+i]=max(dp[now][k+1][j+i],dp[last][k][j]+a[i]);//选择进攻,递推状态
					dp[now][k][j]=max(dp[now][k][j],dp[last][k][j]+max(k*c[i],(j-k*i)*b[i]));//不进攻,k,j不变,但是造成伤害增加 
				}
			}
			for(int k=n-i;k>=1;k--)
				for(int j=(n+n-k+1)*k/2;j>=(i+i+k-1)*k/2;j--)
					dp[last][k][j]=0;//滚动数组用完及时清空 
		}
		for(int k=1;k<=n;k++)
			for(int j=1;j<=5050;j++)
				ans=max(ans,dp[1][k][j]);//枚举k,j。选择递推到1时的最大值 
		cout<

 

这么看来并不是很难,然而现场推了快两个小时还wa2发,罚时差1h银尾,好菜啊。QAQ

你可能感兴趣的:(2018 ICPC EC-final i题Misunderstood Missing)