来一起学习01背包!!!

文章介绍了01背包问题的概念,它是一个物品装入有限载重背包,追求最大价值的优化问题。通过动态规划而非回溯法可以更高效地解决此类问题,避免了暴力搜索的效率低下。动态规划的解决方案通过二维或一维数组记录子问题状态,并利用递推公式更新状态,达到优化时间复杂度的目的。文章还提供了具体的代码示例来演示如何实现这一过程。
摘要由CSDN通过智能技术生成

学习01背包,需要回答以下几个问题:

1.什么是01背包问题?

2.怎样解01背包问题?

3.为什么会要利用动态规划求解01背包问题?

4.怎样利用动态规划求解01背包问题?

5.还可以怎样优化?

接下来我将通过以上五个问题来分析01背包问题:

1.什么是01背包问题?

01背包问题是一个具体案例但却代表着一类问题,我认为他其实是类似于高中物理题目中的物理模型,他更多的是一种模型,是一种思想。

01背包问题描述:

01背包是在M件物品取出若干件放在载重为W的背包里,每件物品的重量为W1,W2至Wn,与之相对应的价值为P1,P2至Pn。每件物品的数量只有一个且物品不可拆分,现要在保证背包不超重的前提下,保证背包内所装物品的总价值最大。

需要注意的是,01背包中,由于每种物品只有一个且不可拆分,所以物品对应的状态只有两种,即放入背包和不放入背包,这一点类似于计算机二进制,非0即1的特性,所以被称为01背包问题。

2.怎样解01背包问题?

拿到这个问题,最先想到思路那一定就是,我把所有的物品,排列组合装入背包,找到不超重并且价值最大的那种装法,那么问题就解决了。这其实是一种回溯的思想,回溯的本质就是一种暴力搜索。

3.为什么会要利用动态规划求解01背包问题?

以上这种暴力解法存在一个问题,那就是难以避免的多层循环嵌套,就算使用回溯算法,那效率也是相当低的。当问题规模变大,需要装入的物品数量增多时,就会没有办法应对,因此便有了动态规划求解01背包问题。

4.怎样利用动态规划求解01背包问题?

我们来设定一个具体的01背包问题例子:

现有一个背包,载重为4,有物品0,物品1,物品2三种物品,每种物品只有一个,且物品不可拆分,求背包所能放入的最大价值。

物品      重量      价值

0              1           15

1              3           20

2              4           30

首先使用动态规划求解01背包问题,要先确定出我们的dp数组,并弄清楚它的含义,dp数组就是记录每个子问题所对应的状态,然后我们利用递推方程,通过前面子问题的状态,去推导后面问题的状态。

对于01背包问题,我们求解的是载重为M的背包所能装得的最大价值。那么我们来考虑,如果背包载重为0,那么显然装入物品最大价值就为0,如果只装入了一个物品,并且能装下,那么最大价值显然就是这一个物品的价值,所以很明显,我们需要一个二维数组,分别来记录我们装入的物品种类和背包的载重量,也就是dp[ i ][ j ],那他表示的含义,就是当我的背包当前的载重为 j 时,装物品0到物品i这些物品,所能装得的最大价值。

这里我简单做了一个表格来说明:

 那么如何推导递推方程呢?对于每一个物品,我们都有两种选择,就是放入背包和不放入背包。

不放入背包的话,那么此时dp[ i ][ j ],与dp[ i-1 ][ j ]的状态是完全一致的,因为这个物品没有放,那么载重j不变,与放物品0到i-1的状态一样。

放入背包的话,那么此时背包载重就变为了 j-weight[ i ],他的状态就等于载重为j-weight[ i ]的背包装0到i-1种物品的价值加上要装的这个物品的价值,也就是dp[ i-1 ][ j-weight[ i ] ]+value[ i ]

而在dp[ i ][ j ]这个状态的最大价值就是装与不装两种情况中的最大值。

需要注意一点,如果当前背包容量放不下这个物品,那么只有不放这一种情况。

接下来看一下具体的代码实现:

#include<stdio.h>
//三种物品
//重量分别 1,3,4
//价值分别15,20,30
//每种物品有一个,背包最大重量为4
//
//dp[i][j] 表示在重量为j的背包中放0—i这些物品,最大价值数




int max(int a, int b)
{
	if (a > b)
		return a;
	else
		return b;
}
int main()
{
	int MaxWeight = 4;
	int weight[] = { 1,3,4 };
	int value[] = { 15,20,30 };
	int dp[3][5] = { 0 };
	int row = 3;
	int col = 5;
	for (int i = 0; i < row; i++)
	{
		dp[i][0] = 0;//在重量为0的背包中,能放的最大价值都为0;
	}
	for (int j = 0; j < col; j++)
	{
		dp[0][j] = 15;//如果只放物品0,那么价值最大都为15,因为物品0只有一个
	}
	for (int m = 1; m < row; m++)
	{
		for (int n = 1; n <= MaxWeight; n++)
		{
			if (n < weight[m])//现在的背包容量放不下当前物品
			{
				dp[m][n] = dp[m - 1][n];
			}
			else
			{
				dp[m][n] = max(dp[m - 1][n], dp[m - 1][n - weight[m]]+value[m]);
			}
		}
	}
	printf("背包最大所能放的最大价值为%d\n", dp[2][4]);
}

5.还可以怎样优化?

对于01背包问题,还有一种利用一维数组实现的方法,其原理是:

我们观察递推公式

max(dp[ i-1 ][ j ],dp[ i-1 ][ j-weight[ i ] ]+value[ i ])

可知每一个背包载重的状态都是由i-1行,也就是上一行推导而来的,那么我们其实可以通过上一行的结果求解当前行状态,直接覆盖在上一行的位置上,从而实现将二维数组压缩为一维数组,但是在覆盖时需要注意一点,由于当我们实现放入物品这种情况时,我们需要[ j-weight ]也就是前面的状态来推导,如果我们从前往后遍历,那么就会把前面的状态全部都覆盖掉,这样就不能得到正确的结果,所以我们在遍历背包载重时,需要从后往前遍历,以便前面的状态是没有被覆盖的状态。

以下是滚动数组方式实现代码:

#include<stdio.h>
//三种物品
//重量分别 1,3,4
//价值分别15,20,30
//每种物品有一个,背包最大重量为4
//
//dp[i][j] 表示在重量为j的背包中放0—i这些物品,最大价值数




int max(int a, int b)
{
	if (a > b)
		return a;
	else
		return b;
}
int main()
{
	int MaxWeight = 4;
	int weight[] = { 1,3,4 };
	int value[] = { 15,20,30 };
	int dp[5] = { 0 };
	for (int i = 0; i < 3; i++)//遍历物品
	{
		for (int j = 4; j >= weight[i]; j--)//背包载重最大为4,最小要等于当前要装入的物品
		{
			dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);//状态转移方程
		}
	}
	printf("背包所能装载的最大价值为%d", dp[4]);
}

以上便是个人对于01背包问题的见解与总结,如果其中存在问题、错误,请直接在评论提出,以便我可以及时修改,谢谢大家!!!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

枣丶睡

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

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

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

打赏作者

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

抵扣说明:

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

余额充值