2018年ecfinal-i题

题目链接 : 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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;
}
}

谢谢你请我吃糖果

你可能感兴趣的:(2018年ecfinal-i题)