背包问题,动态规划,1 0背包,思路详解 带例题优化

动态规划0、1背包问题,
在背包问题中,我们需要在固定的背包容量中,尽可能的装入价值更大的物品,对于某件物品的选择我们需要考虑它是选择装入背包还是不装。所以,我们称此类问题为0、1背包。
在此类背包问题中,要理解三个要素,其一是背包总重量C,其二是物品数量N,其三是物品的价w[]值。
按照动态规划的思路:将大的问题转换成小的问题求解。
我们的大问题是将“对于N件物品,在总重不超过C的前提下,求出最大总价值”
把大问题转换为小问题:对于当前物品,我是选还是不选?在两个选择中选出得到价值最大的存入表中。
选: 那么我们需要在接下来的N-1件物品中选出,总重量不超过C - v[i] (当前物品重量),那么此时当前物品的重量得到的最大价值是:当前物品的价值 + C-v[i] 重量时的最大价值
不选: 那么我们需要在接下来的N-1件物品中选出,总重量不超过C的最大价值物品,那么当前的最大价值就是:0
可以看出,这里的最大价值是将0-C每一个重量级所获得的最大价值都求出来,在使用是直接拿出来(高级打表),这也符合动态规划的思想(不做多余的事),那么当我们选择当前重量的物品时,所获得的最大价值,就是 当前重量物品的价值 加上 背包总重量 减去 当前物品重量所得重量的最大价值之和
接下来结合题目做具体说明:

采药(P1048)

题目描述

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

输入格式

第一行有 2 个整数 T(≤ T ≤ 1000)和 M(1 ≤ M ≤ 100),用一个空格隔开,T 代表总共能够用来采药的时间,M 代表山洞里的草药的数目。

接下来的 M 行每行包括两个在 1到 100 之间(包括 1和 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

输出在规定的时间内可以采到的草药的最大总价值。

输入输出样例
输入

70 3
71 100
69 1
1 2

输出

3

题目分析

其实已经不用分析了,通过前面的铺垫动动手指都知道这是背包问题。
那么既然知道是背包问题,那么就拿出套路:找状态转移方程,各个数据之间的关系。
题目中 ,T表示总时间(总容量),M表示草药的数量(物品数量),接下来的每一组数据,包含当前草药需要的时间v[ ] 和价值w [ ]
对于一个某一种草药,是摘还是不摘?草药的时间很有可能是大于规定时间的,所以遇到采药时间>总时间时直接放弃。如果摘取M[i] 草药,那么接下来的M-1数量中,只能在剩余时间T-v[i] 中采到价值最大的草药。我们用i表示第i棵草药,j表示草药的时间 构建一个二维数组dp[ ][ ] ,dp[i][j]表示第i课草药价值为j的最大价值。对于摘还是不摘,在前面已经提到,不摘那么它的最大价值就是本身dp[i]/[j],摘那么就是需要在dp[i-1][j-v[i]]+w[i];中选出最大价值。可得状态转移方程:dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i])
举例:给出数据结合使用
5(总时间)
3(数量)
2 5
3 8
1 3
当第i件物品时间为j的最大价值时多少。
当只有1件物品时,时间为j时选、还是不选,他的价值最大;

j为时间,i为数量,价值请参照上面数据
数量 /时间j=0j=1j=2j=3j=4j=5
i=0000000
i=1005555
i=200581313
i=303581313
注意:当物品数量为0时和时间为0时,我的最大价值都是0,前者我没有选择的物品,后者我没办法装。

当i=1时,一定是当时间足够时我才会选,也就是从时间j=v[1]时,因为这个物品我只选一次,那么从v[ 1 ]到T他的价值都是w[ 1 ]。
当i=2时,同样是需要时间足够才会选,所以,去计算时同样时从v[ 2 ]开始填,我的最大时间,能否满足(T>=v[ 2 ]),当前最大价值,应该是在当前时间-当前物品需要时间 的时间,最大价值+当前物品最大价值 ,这句话需要结合数据在表中进行理解。可以得到的是dp[i-1][j-v[[i]]+w[i]
如果不选,那么得到的最大价值就是dp[i][j]当前时间的最大价值。

以上,结合代码:
//i为数量,j为时间
#include<iostream>
using namespace std;
const long long M=1005;
long long t,n;
long long w[10001],v[M],dp[M][M]; 

int main(){
	cin>>t>>n;
	for(int i=1;i<=n;i++){
		scanf("%i%i",&v[i],&w[i]);
	}
	for(int i=1;i<=n;i++){
		for(int j=t;j>=0;j--){//从后往前填表
			if(j>=v[i]) //当前的时间小于或等于总时间时才做选择否则为上一个(数量)的最大值 
			dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);//这个转移方程请参照图标想象模拟理解 
			else dp[i][j]=dp[i-1][j];
		}
	}
/*	for(int i=1;i<=n;i++){
		for(int j=1;j<=t;j++){
			cout<<dp[i][j]<<" "; 
		}
		cout<<endl;
	}*/
	cout<<dp[n][t];
	return 0;
}
优化为一维:
//一维数组解法
#include<iostream>
using namespace std;
int t,n;//t表示总时间,n表示总数量
int w[101],v[1001],dp[1001];//w表示每一株的价值,v表示每一株需要的时间,dp表示每一个时间的最大价值

int main(){
	cin>>t>>n;
	for(int i=1;i<=n;i++){
		scanf("%i%i",&v[i],&w[i]);
	}
	for(int i=1;i<=n;i++){
		for(int j=t;j>=v[i];j--){//从后面往前面填
			dp[j]=max(dp[j],dp[j-v[i]]+w[i]);//找出不选,选那个价值最大
		}
	}
	cout<<dp[t];//输出最终的值
	return 0;
}
后记:

在面对0、1背包问题时,主要问题就是选还是不选的问题,其实就是将每一个时间(重量),都求出最大的,在需要的时候直接提取出来可以使用了,当前面的n-1个时间(重量)都求出来了,对于n的最大时间(重量)就简单了。如何求,在考虑这个问题时,我们可以先把问题尽量缩小(类似递推、递推思想),先来考虑一下,当总时间为0,总容量为0时的最大价值,在来考虑他们分别为1时,的最大价值,逐渐往后推,在找出状态转移方程。

有问题请私信qaq
感谢阅读~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给包番茄酱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值