题目链接 : https://codeforces.com/gym/102056/problem/I
当时想了半个小时想了一个毫无意义的状态,结果自信满满写完样例都过不了,结束后才知道状态都是错的…
题意
给出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回合后最多可以造成多少伤害
思路:
看了题就知道是dp,定义状态确实难想,dp[i][j][k]表示从第i回合开始,i到n回合攻击了j次,这j次攻击的回合下标相加为k,同时第一维要用滚动数组优化。因为dp的无后效性,所以回合要从大到小遍历。
首先初始化为dp[n&1][1][n]=a[n],因为最后一回合一定会选择造成伤害。每次可以通过前两维计算出第三位合法的上下界,状态转移为
1 2 3
| dp[i & 1][j + 1][k + i] = max(dp[i & 1][j + 1][k + i], dp[(i + 1) & 1][j][k] + a[i]); dp[i & 1][j][k] = max(dp[i & 1][j][k], dp[(i + 1) & 1][j][k] + (k - j * i)*b[i]); dp[i & 1][j][k] = max(dp[i & 1][j][k], dp[(i + 1) & 1][j][k] + j * c[i]);
|
最后取dp[1][1到n][0到5000]的最值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include<iostream> #include<cstdio> #include<vector> #include<cstring> #include<string> #include<algorithm> #include<set> #include<cstdlib> #include<map> #include<queue> #include<cmath> using namespace std; typedef long long ll; ll dp[2][110][5100]; ll a[110], b[110], c[110]; int main() { int n; int t; scanf("%d", &t); while (t--) { scanf("%d", &n); memset(dp, 0, sizeof(dp)); for (int i = 1; i <= n; i++) { scanf("%d%d%d", &a[i], &b[i], &c[i]); } dp[n & 1][1][n] = a[n]; for (int i = n - 1; i >= 1; i--) { for (int j = 1; j <= n - i; j++) { int down = (i + i + j)*(j - 1) / 2 + n, up = (n + n - j + 1)*j / 2; for (int k = down; k <= up; k++) { dp[i & 1][j + 1][k + i] = max(dp[i & 1][j + 1][k + i], dp[(i + 1) & 1][j][k] + a[i]); dp[i & 1][j][k] = max(dp[i & 1][j][k], dp[(i + 1) & 1][j][k] + (k - j * i)*b[i]); dp[i & 1][j][k] = max(dp[i & 1][j][k], dp[(i + 1) & 1][j][k] + j * c[i]); } } } ll ans = 0; for (int j = 1; j <= n; j++) for (int k = 1; k <= 5050; k++) ans = max(ans, dp[1][j][k]); cout << ans << endl; } }
|