https://www.luogu.org/problemnew/show/P1156
题解
这题确定状态很关键,一开始我想的是将时间和生命作为状态,这样需要枚举的便是时间和生命的大小,这些都是不确定的值,而且最大可能到达3000以上,并且一个时间点可能有多个垃圾,每个垃圾又有吃与堆两种选择,遂凉,不可做。但竟然混了82分。
其实这就是一个背包问题,不必要考虑时间轴,生命值其实就相当于时间轴,如果状态中的生命值小于第i个垃圾到第i+1个垃圾所需的生命值,那么这个状态便可舍弃。一个垃圾有高度和生命两个值,可以将状态设置能前i个垃圾堆到高度为h时的最大生命值是多少。这样的话只需枚举垃圾数和高度。要注意的细节便是要先将垃圾按时间从小到大排序,这样才能保证无后效性。当高度为d时且生命值大于等于0,就可以退出了。活不了的情况下可以另行处理,即每次都吃垃圾,吃到不能吃为止。
代码
#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 5000+5;
const int INF = 0x3f3f3f3f;
int dp[maxn][maxn];
struct Trash{
int t,f,h;
bool operator < (const Trash& rhs) const {
return t < rhs.t;
}
}a[maxn];
int sum[maxn];
int d,g;
int main() {
scanf("%d%d", &d, &g);
int t,f,h;
for(int i = 1; i <= g; ++i) {
scanf("%d%d%d", &a[i].t, &a[i].f, &a[i].h);
sum[a[i].t] += a[i].f;
}
sort(a+1,a+1+g);
memset(dp, -INF, sizeof dp);
dp[0][0] = 10;
for(int i = 1; i <= g; ++i) {
for(int j = d; j >= 0; --j) {
if(dp[i-1][j]-(a[i].t-a[i-1].t) < 0) continue;
// 吃
dp[i][j] = max(dp[i][j], dp[i-1][j]+a[i].f-(a[i].t-a[i-1].t));
// 堆
dp[i][j+a[i].h] = max(dp[i][j+a[i].h], dp[i-1][j]-(a[i].t-a[i-1].t));
if(dp[i][j+a[i].h] >= 0 && j+a[i].h >= d) {
cout << a[i].t << endl;
exit(0);
}
}
}
int die = 10;
for(int i = 0; i <= die; ++i) {
die += sum[i];
}
cout << die << endl;
return 0;
}