【动态规划】01背包问题+查找背包物品

目录

一、0-1背包问题

二、问题分析

1、确定备忘录的具体含义

2、状态转移方程

3、初始化

4、遍历顺序及输出

5、回溯法求解最大价值时的背包物品 

三、总结 

四、完整代码

一、0-1背包问题

给定gif.latex?n种物品(每种物品均只有一个)和一背包。物品i的重量是gif.latex?Wi,其价值为gif.latex?Vi,背包的最大容量为gif.latex?c。怎样选择装入背包中的物品,使得其总价值最大?

例如:现有4种物品,其对应的重量和价值如图所示,另有一最大容量为5的背包,求该背包所能装下物品的最大价值?

物品 重量价值
024
113
246
335

二、问题分析

1、确定备忘录的具体含义

dp[i][j]:任取第0~i件物品,放入容量为j的背包,能得到的最大价值
例:dp[1][2]=3的含义:
	任取物品0~物品1,放入容量为2的背包,能得到的最大价值(取物品1放入背包,其价值最大,为3)

dp[i][j] 的定义十分重要,状态方程的书写、数组初始化、遍历顺序和输出结果都必须严格按照该定义进行书写

2、状态转移方程

对于物品gif.latex?i,它只有放与不放两种状态,当它能放入背包时,状态转移方程只需取两种状态的最大值; 否则,取不放入时的最大价值。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAd3VzaGVuZ19oYXBweQ==,size_20,color_FFFFFF,t_70,g_se,x_16

if (j < w[i])      
	dp[i][j] = dp[i - 1][j];
else               
	dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);

 3、初始化

从状态转移方程可知,欲求dp[i][j],需先确定dp[i-1][j]和dp[i-1][j-w[i]]的值(即需保证dp[i][j]的左上角数据已经处理完成),故我们需对第0行和第0列进行初始化.

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAd3VzaGVuZ19oYXBweQ==,size_20,color_FFFFFF,t_70,g_se,x_16

//初始化第0列(即背包容量为0)
for (i = 0; i < n; i++)
	dp[i][0] = 0;
//初始化第0行(即只有物品0)
for (j = 1; j <= max_weight; j++) {
	if (w[0] <= j) 
		dp[0][j] = v[0];
	else
		dp[0][j] = 0;
}

4、遍历顺序及输出

遍历顺序:求解dp[i][j]只需提前知道其左上角的数据,故以下两种遍历方式均可。

1、先遍历背包再遍历物品(即物品数固定,背包容量逐增至最大,物品再加1)

 2、先遍历物品再遍历背包(即背包容量固定,物品数逐增至最大,背包容量再加1)

输出:dp[n-1][max_weight]: n件物品任取,放入容量为max_weight的背包,所得的最大价值。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAd3VzaGVuZ19oYXBweQ==,size_20,color_FFFFFF,t_70,g_se,x_16

//先遍历物品再遍历背包
for (i = 1; i < n; i++) {                  //遍历物品
	for (j = 1; j <= max_weight; j++) {    //遍历背包
		if (j < w[i])      
			dp[i][j] = dp[i - 1][j];
		else               
			dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
	}
}
//先遍历背包再遍历物品
for (j = 1; j <= max_weight; j++) {    //遍历背包
	for (i = 1; i < n; i++) {          //遍历物品
		if (j < w[i])
			dp[i][j] = dp[i - 1][j];
		else
			dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
	}
}

5、求解最大价值时的背包物品 

由状态转移方程(dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]))可知,当dp[i][j]\neqdp[i-1][j]时,物品i被放入背包中(判断条件),当dp[i][j]=0时,背包中已经没有物品(结束条件)。

//回溯法求解装入物品
i--; j--;     //循环结束后,i=n,j=max_weight+1,故使用dp[n - 1][max_weight]须自减1
cout << "最大价值时的背包物品为:" << endl;
while (dp[i][j]&&i>=0) {    
	if (dp[i][j] != dp[i - 1][j]) {
		cout << "物品" << i << endl;
		j -= w[i];  //装入物品i后背包的最大容量
	}
	i--;    //物品i已经处理完成,接下来讨论物品i-1
}

1、 使用dp[i][j](最大价值)前一定要对i,j进行处理,否则,运行时会抛出异常;

2、结束条件一定要加上i>=0,否则,运行结果可能会出现物品-1等错误。

3、该法得到的结果只为其中的一组最优解,可能还存在其他最优解,例如:当我们装入物品0(2,4)和物品3(3,5)时,其最大价值也为9.

三、总结 

 1、0-1背包问题是背包问题的基础。深入了解0-1背包问题(每件物品只有一个)是掌握完全背包问题(每件物品有无数个)、多重背包问题(不同物品数量不同)和分组背包(物品分为多组,每组最多选一个)的关键;

2、使用dp[i][j]数组时,一定要注意数组的界限问题,避免越界访问造成错误。比如:刚开始的时候,我认为背包不存在容量为0的情况,故没有将第一列初始化为0,导致输出出错,将dp[i][j]全部打印出来后,才知道是dp[1][1]出错了,因为dp[1][1]=max(dp[0][1],dp[0][0]+v[0]),需要用到dp[0][0],造成越界。所以,检验算法的正确性,可以先人工求解出dp[i][j]数组,再用程序将其打印出来,这样有利于我们快速找到问题所在。

1e96f5966f5f45099dcf8a9880f4b971.png

                                                                  转换表(打印)

转换表(人工)
 012345
0004444
1034777
2034779
3034789

3、背包问题一定要先明确辅助数组的具体含义再确定转换方程,根据转换方程来确定初始化、遍历顺序和输出结果。

四、完整代码

#include<iostream>
#include<algorithm>
using namespace std;

const int max_weight = 5;
const int n = 4;

int main() {
	int i, j;
	int w[n] = { 2,1,4,3 }; //重量
	int v[n] = { 4,3,6,5 }; //价值
	int dp[n][max_weight+1];//辅助数组

	//初始化第0列(即背包容量为0)
	for (i = 0; i < n; i++)
		dp[i][0] = 0;
	//初始化第0行(即只有物品0)
	for (j = 1; j <= max_weight; j++) {
		if (w[0] <= j) 
			dp[0][j] = v[0];
		else
			dp[0][j] = 0;
	}

	//状态转移
	//先遍历物品再遍历背包
	for (i = 1; i < n; i++) {                  //遍历物品
		for (j = 1; j <= max_weight; j++) {    //遍历背包
			if (j < w[i])      
				dp[i][j] = dp[i - 1][j];
			else               
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
		}
	}

	//输出
	cout << dp[n - 1][max_weight] << endl;

	//回溯法求解装入物品
	i--; j--;     //循环结束后,i=n,j=max_weight+1,故使用dp[n - 1][max_weight]须自减1
	cout << "最大价值时的背包物品为:" << endl;
	while (dp[i][j]&&i>=0) {    
		if (dp[i][j] != dp[i - 1][j]) {
			cout << "物品" << i << endl;
			j -= w[i];  //装入物品i后背包的最大容量
		}
		i--;    //物品i已经处理完成,接下来讨论物品i-1
	}

	return 0;
}

运行结果: 

  • 17
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
动态规划背包问题在MATLAB中的实现可以考以下步骤: 1. 定义变量和参数:首先,需要定义物品的数量、背包的容量、物品重量和价值的向量。例如,假设有n个物品背包容量为C,物品重量向量为W,价值向量为V。 2. 初始化动态规划表格:使用一个二维数组V来表示动态规划表格,其中V(i,j)表示前i个物品背包容量为j时能够得到的最大价值。初始化V的第一行和第一列为0,表示没有物品背包容量为0时的最大价值为0。 3. 动态规划过程:使用循环来填充动态规划表格V。根据动态规划的状态转移方程,可以得到以下代码: ``` for i = 1:n for j = 1:C if W(i) > j V(i,j) = V(i-1,j); else V(i,j) = max(V(i-1,j), V(i-1,j-W(i)) + V(i)); end end end ``` 4. 输出结果:最后,可以通过查找V的最后一个元素V(n,C)来得到在给定背包容量下的最大价值。 请注意,上述代码只是一个示例,具体的实现可能会根据实际情况略有不同。在实际应用中,还可以通过追踪选择的物品以及它们的数量来得到最优解。 总结起来,动态规划背包问题的MATLAB实现包括定义变量和参数、初始化动态规划表格、进行动态规划过程以及输出结果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [【背包问题】离散粒子群算法求解0-1背包问题【含Matlab源码 1342期】](https://blog.csdn.net/weixin_63266434/article/details/129400525)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值