【0-1背包】小白也能看懂的0-1背包问题(举例分析+附C++代码)

本文通过实例解析0-1背包问题,介绍穷举法和画表格两种方法,配合C++代码演示,帮助初学者理解背包问题的解决策略,强调最优子结构的重要性。
摘要由CSDN通过智能技术生成

欢迎你!

如果让一个初学者直接硬啃0-1背包的代码和公式,那确实有些强人所难。
我们开场不讲公式定理
先入手一个例子
从一个具体问题出发来研究一个0-1背包问题:

【问题描述】一个0-1背包问题

已知一个【背包】,最多可装总重为 5 的物品。
还有3个【物品

物品价值重量
物品A601
物品B1002
物品C1203

这三个物品数量都为1

现在要将这些物品装入背包,在不超重的情况下,如何装能够获得最大的价值?求这个最大价值。

【方法1】穷举

最简单的方法就是画一个树型的图来逐个判断每个选择的分支。

基本思路可以是:
“先判断是否装A物品,再判断是否装B物品,再是否装C……”

这里给出这个图:
0-1背包的穷举判断
这个过程简单有效,便于我们理清思路。
基于这个图我们可以快速观察到价值最大的装法。

但每次都画一棵树是不行的,不然随便一个装7、8个物品的同类问题,都会让草稿纸的宽度受到严峻挑战。

我们画一个表格试试看,这个表格最终可以帮你弄懂0-1背包算法的原理
如果手边有草稿纸的话,你也可以一起画一画

【方法2】画表格

由于背包承重为5,所以表格我们设计5列,每列表示背包当前最多能装多重的东西,分别包含从一至五的五种情况:1、2、3、4、5
由于有3个物品要装,所以表格我们设计3
暂时先不要深究为什么这么设计,我们一点一点来

画好这个表格之后,我们给它取名为Bag,如下图:

开始绘制表格
和前面画的那颗树的思路一样,第一步我们先判断是否装物品A的情况,所以第一行的的纵坐标我们命名为“只考虑A”。当前操作的物品为A
表格第一行中每一格的含义代表:“在只考虑装物品A,且背包容量最大为1、2、3、4、5这几种情况下,背包所装物品获得的最大价值

如下图表格,在只考虑A,且背包最大容量为1的情况下,很明显,直接将重量为1、价值为60的物品A装进去,就是当前情况能获得的最大价值了,即Bag[1][1] = 60
同理,只考虑A,在背包承重为2的情况下,也只能放入一个A物品,即Bag[1][2] = 60
注意,每个物品数量只有一个,所以在只考虑A的情况下,只能放入一个A物品
0-1背包表格第1行

很简单,在只考虑A的情况下,很快就能填完第一行,结果见下图:
表格第二行,我们将“在判断是否装A物品的结果上,进一步考虑是否装B物品”,所以第二行的纵坐标我们命名为“基于A考虑B”
当前物品从A变为B
基于A考虑B

如下图,在考虑B物品的情况下,对于Bag[2][1](即:在考虑B、且背包容量为1时可获得得最大价值),要沿着以下三个基本思路考虑:

1、能不能装入B物品?
2、需不需要装入B物品?
3、如果装入B物品,怎么装能够获得最大价值?

显然在背包承重为1的时候,无论怎样都不能装入重量为2的B物品,
那么这种情况下该如何决定Bag[2][1]的值呢?虽然背包承重为1时操作不了B物品,但上一轮表格中统计了各个背包承重下基于A物品的最佳结果。所以可以用第一行的结果来代替它。
找到上一轮(也就是表格第一行)中背包承重为1的最大价值——即Bag[1][1], 作为Bag[2][1]的结果。如下图:
0-1背包表格第二行--1
可以总结出第一条规律:当前物品装不了,就从“头顶”照抄

接下来是重头戏:Bag[2][2]:基于A考虑B,且背包容量为2的情况下能得到的最大价值
当前背包容量为2,物品B的重量为2,意味着我们有条件装入B物体,但是需不需要装入,要用以下几条思路思考:

1、能不能不装入B物品?
2、如果一定要装入B物品,该怎么调整背包内容?
3、上述可能的装法中哪种装法价值最大

结合上面的思路:

1、可以不装入B物品,那么Bag[2][2]的一个值可以是Bag[1][2](上一轮只操作A物品且背包容量为2的最大价值),即60
2、如果一定要装入B物品,就需要腾出B物品等重量的空间。当前背包承重为2,为B物品腾出重量为2的空间后,这个背包的承重就变为0了,此时我们再将物品B放入背包中,这个背包里现在就只装有B物品了,并且背包的价值为100。所以Bag[2][2]的另一个值可以是100
3、比较上述两种情况产生的价值大小,显然“先腾地方、再装入B物品”的方法产生了最大的价值。所以将100填入Bag[2][2]

分析结果见下表:
0-1背包表格第二行-0

同理,我们继续来看Bag[2][3]:基于A考虑B,且背包容量为3的情况下能得到的最大价值
如下图,相信你已经找到一些规律了:
如果不装入B物品,则直接从头顶处的邻居找答案。即Bag[2][3] = Bag[1][3],为60
如果一定要装入B物品,注意,此时为B物品背包腾出2点空间后,剩余1点承重。这1点承重如果被最大化利用,取得了最高的价值,那么我们基于那个结果再装入B物品,就能取得“腾地方”这种方法的最大价值。
如何找到在仅有一点承重情况下背包可装的最大价值呢?这个答案就存储在Bag[1][1]中,即上一轮操作中背包承重为1情况下背包可装的最大价值。(在下图中用蓝色椭圆圈出)
所以在“腾地方”这种方法下,Bag[2][3]的值就是用Bag[1][1]加上物品B的价值,即60+100,得到160
最后,不装入B物品会获得60的价值,腾完地方再装入B物品会获得160的价值,所以最终将160填入Bag[2][3]
0-1背包表格第二行-1
讲到这里,相信你已经明白了解决0-1背包的基本思路:基于前面小问题的最好结果来进一步获得大问题的最好结果

这也就是这一问题我们常听到的最优子结构性质。

现在我们接着完成第二行的表格。有了前面的经验,完成剩余内容应该不难。
要么从头顶照抄。要么先给B物品腾地方、找最优,然后再装。最后比谁更大
计算过程和结果如下表所示:
0-1背包表格第二行-2

在完成最后一行表格之前,我们来总结一下填写这个表格的规律:
0-1背包公式
如果进一步抽象,将物品的价值用v、重量用w这样的数组来表示,就有了下面这个最终的公式:
0-1背包最终公式

现在,来完成最后一行的Bag吧。
注意,当前物品为C,所以每次尝试为C “ 腾地方 ” 的时候,需要腾出3点重量空间。
下表展示了部分计算过程:
在这里插入图片描述

“ 表格填完了,然后呢?”

还记得我们最开始的问题吗?

在背包承重上限为5的情况下,有ABC三个物品,每个物品各一个,怎样装能获得最大价值?求这个最大价值。

所以,现在需要在表格中,找考虑了ABC三个物品,且背包最大承重为5的那一格中存储的数字,就是题目要求的答案。即Bag[3][5]中存储的220

我们填写整个Bag表格,最终的目的就是为了获得Bag[3][5]这个位置的值。

Congratulations!你已经学会了解决0-1背包的方法。

一些扩展

【1】0-1背包的表格最完整的形式应该和下图中的一样。多添加了横竖为0的行列。尝试思考一下为什么要这么设计
添加0行
【2】如果先考虑C、再考虑A、最后再考虑B,结果是一样的吗?

源码(C++):

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

int main()
{
	int n, v;	//n:物品数量,v:背包最大容量 
	
	cout << "请输入物品数量n 和背包容量v" << endl;
	cin >> n >> v;
	vector<int> w(n+1);	//第n件物品的重量 
	vector<int> c(n+1);	//第n件物品的价值 
	vector<vector <int>> bag;
	for (int i = 0; i <= n; i++) {  //初始化二维Bag表格,额外创建全0的第0行和第0列
		vector<int> vec_row(v + 1);
		bag.push_back(vec_row);
	}
	
	cout << "请按顺序输入物品价值c 和物品重量w" << endl;
	for (int i = 1;i <= n;i++) //从1位置开始存储数据,0位置存储0
		cin >> c[i] >> w[i];

	for (int i = 1; i <= n;i++) { //从1位置开始遍历
		for (int j = 1;j <= v;j++) { //从1位置开始遍历
			if (w[i] > j)   //如果当前物品重量大于当前背包承重,则无法放入,只能从“头顶”照抄
				bag[i][j] = bag[i - 1][j];
			else           //如果可以装,就要判断是“头顶”照抄的价值大,还是“腾地方”找最优、最后装当前物品的价值大。
				bag[i][j] = max(bag[i - 1][j], bag[i - 1][j - w[i]] + c[i]);
		}
	}

	cout << "最大价值为" << bag[n][v] << endl;
	return 0;
}

星砚完成于2024年3月26日
希望本篇的内容对你有所帮助。我们有缘再会!

  • 36
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值