P1156 垃圾陷阱(新手上路)

递增

传送门:

P1156 垃圾陷阱 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路

读完题一看背包变形:n件物品,背包总容量(水井深度),那花销?价值?又是什么

这里有三个供我们选择,其实这里看似很难选,但是我们需要看题目需要的是什么,一般什么就是价值,那么花销一般就与背包容量有关

先看什么是花销,我们这里选定水井深度为背包容量(当然也可以思考别的比如说存活时间,但是发现这是可变值,那我们一般都先选定制来做测试这样dp的时间与空间我们就可以控制了),那么花销就是垃圾能垫高的高度H

其次我们来看价值是什么,其实想都不用想那就是存活时间,而不会是垃圾投放时间,这边垃圾投放时间只能是作为限制我在什么时候才可以遇到垃圾来进行操作罢了,这边略微思考就可以得出。

这边刚开始我们要做个小排序,因为题目中未提及是按照时间先后顺序输入的,所以我们需要有一个时间先后顺序的排序,来以此判断和操作垃圾

struct node{
	int t,f,h;
}a[N];
bool cmp(node x,node y){
	return x.t<y.t;
}
sort(a+1,a+n+1,cmp);

然后就是dp了,我们直接用滚动数组来做了,如果对滚动数组还不太熟悉,可以依照01背包问题的二维数组板子来扩写dp数组部分

首先我们要知道dp[x]表示当前高度为x下的最长可存活时间,那么我们就可以放心大胆的看代码了

//以下代码中rep(i,a,b)表示for(int i=a;i<=n;i++)
//per(i,a,b)表示for(int i=a;i>=b;i--)

dp[0]=10;//题目提及“假设卡门当前体内有足够持续 10 小时的能量”,
//那么初始化高度为0的最长可存活时间为10
rep(i,1,n){//基操
	per(j,d,0){//基操,滚动数组不太熟悉看上面可进行调整
		if(dp[j]>=a[i].t){//当前最长存活时间大于当前物品的掉落时间,上面提到的限制条件就是这个
		//也就是只有活着遇到该物品了才可以对该物品进行操作
		
			if(j+a[i].h>=d){//如果当前高度已超过水井深就已得到答案直接输出,(这就是前面按时间从小到大排序,这里直接遇到答案输出的好处)
				cout<<a[i].t;
				return 0;
			}
			dp[j+a[i].h]=max(dp[j+a[i].h],dp[j]);//堆,状态转移方程,保证当前到了高j+a[i].h的存活时间依旧是当前可达到的最大
			dp[j]+=a[i].f;//吃,这里实则是dp[j]=max(dp[j],dp[j]+a[i].f),但是因为a[i].f恒正所以直接这样写也没关系,
			//为什么要加呢?会有疑问前面不是max状态转移过了吗,那为什么要搞这一个,这一个有什么用呢
			//这里主要为了保证当前高度,当前物件,当前时间(指的是当前垃圾丢放时间)下存活时间是最长的;我们要确保可达到当前高度为j下的最长可存活时间,就是说我们已经到了当前高度,那么我们就没必要继续放入背包(也就是堆的过程,消耗体力),而可以增加价值(也就是增加体力);
			//这边提到没有必要继续堆,和上面那个max状态转移方程是不是有一些歧义,上面的状态转移方程是为了高j+a[i].h的最长存活时间服务的,而这里是为了高j的最长存活时间服务的。
		}
	}
}
cout<<dp[0]<<endl;//当然我看到有题解for找max的,如果不理解当然也可以这样做
//其实就这样想,前面代码不是每次都放了最长可存活时间吗,那高度为0的时候是不是这时候就把能活到那个时间点之前的全部a[i].t加了一边包括自己原来的10;

这里思路可能会写的有点不好理解,写dp的心得其实主要在于用正常的逻辑思维描述出来而不是去考虑其实现细节,初学者比如学背包可以列列表看看不同,但是慢慢得心应手了后需要做到口说中文(也就是逻辑),手写代码

这里思路有问题可以评论或私信我,大家一起学习进步!

AC code

// Problem: 
//     P1156 垃圾陷阱
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1156
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<algorithm>
//#include<cstdio>

#define ll long long 
#define endl '\n'
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define N 110 //1e6+100 
 
using namespace std;
int d,n;
struct node{
	int t,f,h;
}a[N];
int dp[N];
bool cmp(node x,node y){
	return x.t<y.t;
}
int main(){
	//ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>d>>n;
	rep(i,1,n) cin>>a[i].t>>a[i].f>>a[i].h;
	sort(a+1,a+n+1,cmp);
	dp[0]=10;
	rep(i,1,n){
		per(j,d,0){
			if(dp[j]>=a[i].t){
				if(j+a[i].h>=d){
					cout<<a[i].t;
					return 0;
				}
				dp[j+a[i].h]=max(dp[j+a[i].h],dp[j]);
				dp[j]+=a[i].f;
			}
		}
	}
	cout<<dp[0]<<endl;
	return 0; 
} 

有问题请评论或私信我,谢谢!

有共同学习需求的可以加入洛谷团队:https://www.luogu.com.cn/team/66731

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值