P1156 USACO01OPEN 垃圾陷阱
题目描述
卡门――农夫约翰极其珍视的一头 Holsteins 奶牛――已经落到了 "垃圾井" 中。"垃圾井" 是农夫们扔垃圾的地方,它的深度为 DDD(2≤D≤1002 \le D \le 1002≤D≤100)英尺。
卡门想把垃圾堆起来,等到堆得与井深同样高或比井深更高(即,垃圾高度总和 ≥D\geq D≥D)时,她就能逃出井外了。另外,卡门可以通过吃一些垃圾来维持自己的生命。
每个垃圾都可以用来吃或堆放,并且堆放垃圾不用花费卡门的时间。
假设卡门预先知道了每个垃圾扔下的时间 ttt(1≤t≤10001 \le t \le 10001≤t≤1000),以及每个垃圾堆放的高度 hhh(1≤h≤251 \le h \le 251≤h≤25)和吃进该垃圾能增加维持生命的时间 fff(1≤f≤301 \le f \le 301≤f≤30),要求出卡门最早能逃出井外的时间,已知卡门当前体内有足够持续 101010 小时的能量,如果卡门 101010 小时内(不含 101010 小时,维持生命的时间同)没有进食,卡门就将饿死。特别地,若体力值为 000 时吃下垃圾或逃出井外也不会饿死。
输入格式
第一行为两个整数,DDD 和 GGG(1≤G≤1001 \le G \le 1001≤G≤100),GGG 为被投入井的垃圾的数量。
第二到第 G+1G+1G+1 行每行包括三个整数:ttt(1≤t≤10001 \le t \le 10001≤t≤1000),表示垃圾被投进井中的时间;fff(1≤f≤301 \le f \le 301≤f≤30),表示该垃圾能维持卡门生命的时间;和 hhh(1≤h≤251 \le h \le 251≤h≤25),该垃圾能垫高的高度。
输出格式
如果卡门可以爬出陷阱,输出一个整数,表示最早什么时候可以爬出;否则输出卡门最长可以存活多长时间。
输入输出样例 #1
输入 #1
20 4
5 4 9
9 3 2
12 6 10
13 1 1
输出 #1
13
说明/提示
【样例说明】
卡门堆放她收到的第一个垃圾:height=9\mathrm{height}=9height=9;
卡门吃掉她收到的第 222 个垃圾,使她的生命从 101010 小时延伸到 131313 小时;
卡门堆放第 333 个垃圾,height=19\mathrm{height}=19height=19;
卡门堆放第 444 个垃圾,height=20\mathrm{height}=20height=20。
解答
显然,这种题目是 DP\mathrm{DP}DP 题.
状态定义
我们设 dpi,jdp_{i,j}dpi,j 表示处理(吃或者堆起来) 111 ~ iii 的垃圾,当前奶牛维持生命的时间为 jjj 所可以攀爬的最大高度
答案
显然判断是否可以出井的条件就是 dpG,...dp_{G,...}dpG,... 的最大值,形式化的讲,就是
ans=maxi=1r(dpG,i) \mathrm{ans}=\max_{i=1}^{r}\big(dp_{G,i}\big) ans=i=1maxr(dpG,i)
其中 rrr 就是奶牛的生命值可能的最大值,也就是 r=G×maxf+10r=G\times \max f+10r=G×maxf+10 最大为 r=100×30+10=3010r=100\times 30+10=3010r=100×30+10=3010.
方程
很简单,每个垃圾有两种选择.
- 吃下去,回复体力值
- 堆起来
使用刷表法, 方程为:
{dpi+1,j−wait+eat=max(dpi+1,j−wait+eat,dpi,j)(j>wait)dpi+1,j=max(dpi+1,j,dpi,j+height)(j>wait) \begin{cases} dp_{i + 1, j-\mathrm{wait}+\mathrm{eat}}=\max\Big(dp_{i + 1, j-\mathrm{wait}+\mathrm{eat}},dp_{i,j}\Big) \Big(j>\mathrm{wait}\Big) \\ dp_{i+1,j}=\max\Big(dp_{i+1, j}, dp_{i,j}+\mathrm{height}\Big) \Big(j>\mathrm{wait }\Big) \end{cases} ⎩ ⎨ ⎧dpi+1,j−wait+eat=max(dpi+1,j−wait+eat,dpi,j)(j>wait)dpi+1,j=max(dpi+1,j,dpi,j+height)(j>wait)
其中
{wait=两个垃圾等待的时间eat=吃掉当前垃圾回复的体力时间height=垃圾堆放的高度 \begin{cases} \mathrm{wait}=\text{两个垃圾等待的时间} \\ \mathrm{eat}=\text{吃掉当前垃圾回复的体力时间} \\ \mathrm{height}=垃圾堆放的高度 \end{cases} ⎩ ⎨ ⎧wait=两个垃圾等待的时间eat=吃掉当前垃圾回复的体力时间height=垃圾堆放的高度
贪心法输出答案即可
cpp
#include <stdio.h>
#include <algorithm>
#include <string.h>
int D, G;
int _max(int x, int y) {return (x > y) ? x : y;}
void max(int& x, int y) {x = _max(x, y);}
struct Trash_t {
int Fresh_Time; // 刷新时间
int Give_Time; // 提供的时间
int Height; // 高度
bool operator < (const Trash_t& other) {
return Fresh_Time < other.Fresh_Time; // 按照刷新时间排序
}
}trash[128];
int dp[128][3510];
int main()
{
scanf("%d%d", &D, &G);
for (int i = 1; i <= G; i++) scanf("%d%d%d", &trash[i].Fresh_Time, &trash[i].Give_Time, &trash[i].Height);
// 按照刷新时间排序
std::sort(trash + 1, trash + 1 + G);
memset(dp, -1, sizeof(dp));
dp[0][10] = 0;
for (int i = 0; i < G; i++) {
for (int j = 0; j <= 3500; j++) {
if (dp[i][j] == -1) continue;
// 从垃圾 i 等待下一个垃圾的时间
int wait_time = trash[i + 1].Fresh_Time - trash[i].Fresh_Time;
if (j < wait_time) continue;
// 吃掉垃圾
max(dp[i + 1][j - wait_time + trash[i + 1].Give_Time], dp[i][j]);
// 堆垃圾
max(dp[i + 1][j - wait_time], dp[i][j] + trash[i + 1].Height);
}
}
int ans = 0;
for (int i = 0; i <= 3500; i++) {
max(ans, dp[G][i]);
}
// printf("ans=%d\n", ans);
if (ans >= D) {
// 成功
// 找出最短的时间
// 显然, 最短的时间就是某一个垃圾的投放时间
for (int i = 1; i <= G; i++) {
for (int j = 0; j <= 3500; j++) {
if (dp[i][j] >= D) {
// 直接输出垃圾 i 的投放时间
printf("%d\n", trash[i].Fresh_Time);
return 0;
}
}
}
}
else {
// printf("QWQ\n");
// 失败
// 最多可以活多久, 自然是吃掉所有的垃圾
// 模拟计算即可
int time = 10;
for (int i = 1; i <= G; i++) {
int wait_time = trash[i].Fresh_Time - trash[i - 1].Fresh_Time;
if (wait_time > time) {
printf("%d\n", time + trash[i - 1].Fresh_Time);
return 0;
}
time -= wait_time;
time += trash[i].Give_Time;
}
printf("%d", time + trash[G].Fresh_Time);
}
return 0;
}