P1156 [USACO01OPEN] 垃圾陷阱

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=max⁡i=1r(dpG,i) \mathrm{ans}=\max_{i=1}^{r}\big(dp_{G,i}\big) ans=i=1maxr(dpG,i)

其中 rrr 就是奶牛的生命值可能的最大值,也就是 r=G×max⁡f+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;
}
相关推荐
疯狂打码的少年1 小时前
【数据结构】二叉排序树(BST)的定义与操作
数据结构·笔记·算法
lally.1 小时前
整数等差数列超图中的三个非微扰现象
算法
wabs6662 小时前
关于字符串【力扣541.反转字符串II的思考】
数据结构·算法·leetcode·字符串
土司大王2 小时前
LeetCode hot100——移动零
java·算法·leetcode
Tyler_TXZ3 小时前
C++C语言之——树
c语言·c++·算法·
旖旎夜光3 小时前
LeetCode 30:串联所有单词的子串(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口
白狐_7983 小时前
408 数据结构|完全二叉树两道典型题:第 6 层叶结点数与叶结点总数
数据结构·算法
lucas_AI3 小时前
喂张白纸也能吐出证件号?文档 MLLM 的"关系级泄露"被测出来了
人工智能·算法·掘金技术征文
手写码匠3 小时前
华为云Flexus+DeepSeek征文|Dify 多 Agent 灰度发布实战:让每一次变更都“小步快跑、随时可回滚“
人工智能·深度学习·算法·aigc