[DP][背包] 垃圾陷阱

题面

我的 \(DP\) 好菜啊。。。

其实不难看出这个题是背包的,但问题是应该怎样设计转移。

可以看到题目一共给了 \(4\) 个状态:当前时间,当前高度,当前生命,当前垃圾。
那对于这四个状态,我们该如何处理呢?

首先可以考虑到,我们在枚举垃圾的时候,为了保证时间有序从而使维持的生命时间足够转移到下一情况这一限制成立,需要按照垃圾扔下的时间排序。
对时间排序后,当前时间这一维实际就可以消掉了,因为只要某一个状态高度大于 \(d\) 了,由于时间单调,它一定是时间最早的。

那么对于剩余的状态,我们可以进行如下的设计:
f[i][j] 表示当前的垃圾为 \(i\),深度为 \(j\) 时能维持的最大生命时间。

然后就可以很方便的进行背包的转移了,但是注意,当我们不用一个垃圾放置于脚下时,是要将它吃掉的。
如果我们不能登上顶部时,是需要输出 \(10 + 垃圾总时间\) 的。

代码:

# include <iostream>
# include <cstdio>
# include <cstring>
# include <algorithm>
# define MAXG 105
# define MAXD 105
# define MAXT 1005

struct garbage{
	int tim, lif, hei;
}gar[MAXG];

int f[MAXG][MAXD]; // 前 i 个垃圾到高度 j 获得的最长生命时间

bool CmpG(garbage x, garbage y){
	return x.tim < y.tim;
}

int main(){
	int d, g;
	scanf("%d%d", &d, &g);

	for(int i = 1; i <= g; i++){
		scanf("%d%d%d", &gar[i].tim, &gar[i].lif, &gar[i].hei);
	}

	std::sort(gar+1, gar+g+1, CmpG);

	memset(f, ~0x3f, sizeof(f));
	f[0][0] = 10;

	int ans = 0;
	for(int i = 1; i <= g; i++){
		for(int j = d; j >= 0; j--){
			if(f[i-1][j] < gar[i].tim - gar[i-1].tim){ // 活不到
				continue;
			}
			if(j + gar[i].hei >= d){
				printf("%d", gar[i].tim);
				return 0;
			}
			f[i][j+gar[i].hei] = std::max(f[i][j+gar[i].hei], f[i-1][j] - (gar[i].tim-gar[i-1].tim));
			f[i][j] = std::max(f[i][j], f[i-1][j] + gar[i].lif - (gar[i].tim-gar[i-1].tim));
		}
		ans = std::max(ans, f[i][0] + gar[i].tim);
	}

	printf("%d", ans);

	return 0;
}
posted @ 2020-09-25 10:17  ChPu437  阅读(132)  评论(0编辑  收藏  举报