2018 ACM-ICPC EC-Final(Misunderstood … Missing )DP好题~

版权声明:本文为博主原创文章,转载请说明出处。 https://blog.csdn.net/xianpingping/article/details/86378057

链接:https://ac.nowcoder.com/acm/contest/366/I
来源:牛客网
 

题目描述

Warm sunshine, cool wind and a fine day, while the girl watching is pursuing in chaos. Rikka reached out her hand and got the garland on her head, finding LCR with the immortal smile. The dream ended up waking, but the doubts will never disappear. In the end, without knowing about LCR, Rikka was invited to Shuyuan Men, a street of Chinese traditional arts in Xi'an.
``Is it enough to use the stored wires?''
``No problem... Those leaders are only concerned about expanding EC Final for the school's and their `achievements'. All chores are ours. It is fine to simply connect those wiring boards in the series for each row.''
Their conversation engaged Rikka. Feeling strange, she decided to follow them. But before all, she needs to beat the devil in her heart.
Rikka has an aggressivity A and an increment D of it, which are both 0 initially. There are n rounds in total. For i=1,2,…,n

at the beginning of i-th round Rikka's aggressivity A increases by the increment D, and then she can do one of the following:

 ·Attack and cause a damage of (A+ai)
 Use the Omnipotent Garland from LCR to increase the increment D by bi
·Use her Schwarz Sechs Prototype Mark II to increase the aggressivity A by ci

题意:

你有n次操作,每次操作有3种选择,1.造成A+ai点伤害;2.永久给D增加bi;3.永久给A增加ci。(每次操作前执行:A+=D)。问最后最多造成多少伤害。

思路:

很明显是dp。但具体如何dp。。。

状态表示:
dp[i][j][k]表示从后开始执行到第i步,执行了j次操作1,选择操作1的下标和为k所造成的最高伤害。

我觉得这种表示还是很6的。因为最后一次肯定是主动出击(选选择1)的。所以初始化最后一个。从最后一个向前推。

而且为了保证每一个状态的唯一性,在这里用上述的表示方法。

为了使得不超内存,除了滚动数组省去第一维,在这里用奇偶来表示当前的和后边的。

想到了就比较容易。关键是这种状态想不到啊。dp大法好。

具体见代码:

#include <bits/stdc++.h>
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<<ans<<endl;
	}
}

猜你喜欢

转载自blog.csdn.net/xianpingping/article/details/86378057