动态规划 —— 01背包问题

作为动态规划的经典题目,网上的很多解释有点模糊,写个博客记录一下吧。

1、问题描述

你有一个承重能力为 b e a r i n g bearing bearing 的背包,现在有一些物品,其重量按顺序存放在数组 w e i g h t ( w e i g h t [ i ] > 0 ) weight (weight[i]>0) weightweight[i]>0 中,每个物品的价值按顺序存放在数组 v a l u e value value 中。问题:在背包的承重范围内,可以装的物品的最大价值是多少?

2、状态转移方程的建立

2.1 状态转移方程

我们用 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示访问到 i i i 个物品时(包括第 i i i 个物品)承重能力为 j j j 的背包所装物品的最大价值。 【注意:有的文章将 j j j 解释为背包的剩余承重量,这是错误的!这里的 j j j 就是背包的承重能力,至于为什么,看到后面就懂了。】
当访问到第 i i i 个物品的时候,对于承重能力为 j j j 的背包,分为以下两种情况:

  • j < w e i g h t [ i ] j<weight[i] j<weight[i],即背包的承重能力 j j j 小于第 i i i 个物品的重量 w e i g h t [ i ] weight[i] weight[i],此时第 i i i 个物品是无法装入背包中的,因此访问到第 i i i 个物品的时候背包所装物品的最大价值就是访问到第 i − 1 i-1 i1 个物品时背包所装物品的最大价值。即:
    d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j]=dp[i-1][j] dp[i][j]=dp[i1][j]
  • j > = w e i g h t [ i ] j>=weight[i] j>=weight[i],即背包的承重能力 j j j 不小于(大于等于)第 i i i 个物品的重量 w e i g h t [ i ] weight[i] weight[i],此时第 i i i 个物品可以装入背包中【注意:这里说第 i i i 个物品可以装入背包指的是背包之前怎么装的我不管,由于背包的承重能力 j > w e i g h t [ i ] j>weight[i] j>weight[i],因此第 i i i 物品是可以装入背包中,至于装入第 i i i 个物品之前怎么装最好,那是 d p [ i ] [ j − w e i g h t [ i ] ] dp[i][j-weight[i]] dp[i][jweight[i]] 需要回答的问题。】。对于第 i i i 个物品,有两种选择——装或者不装。
    选择装的时候的最大价值为:
    d p [ i − 1 ] [ j − w e i g h t [ i ] ] + v a l u e [ i ] dp[i-1][j-weight[i]]+value[i] dp[i1][jweight[i]]+value[i]
    注意:这里不是 d p [ i − 1 ] [ j − 1 ] + v a l u e [ i ] dp[i-1][j-1]+value[i] dp[i1][j1]+value[i],此处的理解是关键。】
    选择不装的时候的最大价值则为:
    d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j]
    我们选择两者的最大值,即为访问到 i i i 个物品时(包括第 i i i 个物品)承重能力为 j j j 的背包所装物品能获得的最大价值,即 d p [ i ] [ j ] dp[i][j] dp[i][j],也就是:
    d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j − w e i g h t [ i ] ] + v a l u e [ i ] , d p [ i − 1 ] [ j ] ) dp[i][j]=max(dp[i-1][j-weight[i]]+value[i],dp[i-1][j]) dp[i][j]=max(dp[i1][jweight[i]]+value[i],dp[i1][j])

2.2 初始条件

对于 d p [ i ] [ j ] dp[i][j] dp[i][j],由于下标是从0开始的,因此01背包问题的初始条件是:

  1. 由于物品的重量不为0,因此当背包的承重 j j j 为 0 时,访问到第 i i i 个物品时所能装的物品的最大价值为0,即 d p [ i ] [ 0 ] = 0 dp[i][0]=0 dp[i][0]=0.
  2. i i i 个物品处的最大价值与第 i − 1 i-1 i1 个物品的最大价值是相关的,如果首个物品的编号为 i i i,则第 i − 1 i-1 i1 个物品就代表没有物品,由于数组的下标从0开始,因此我们将物品从1开始编号,则第0个物品代表没有物品,因此有 d p [ 0 ] [ j ] = 0 dp[0][j]=0 dp[0][j]=0.

可以验证,上面的初始条件和状态转移方程是吻合的。

3、代码实现

对于如下问题。
物品的重量为:
w e i g h t = [ 4 , 6 , 2 , 2 , 5 , 1 ] weight=[4,6,2,2,5,1] weight=[4,6,2,2,5,1]
每个物品的价值为 :
v a l u e = [ 8 , 10 , 6 , 3 , 7 , 2 ] value=[8,10,6,3,7,2] value=[8,10,6,3,7,2]
背包的承重为:
b e a r i n g = 12 bearing=12 bearing=12
为了适应状态转移方程,我们对物品从1开始编号,而不是从0开始编号,先对 w e i g h t weight weight v a l u e value value 补零,即:
w e i g h t = [ 0 , 4 , 6 , 2 , 2 , 5 , 1 ] weight=[0,4,6,2,2,5,1] weight=[0,4,6,2,2,5,1]
v a l u e = [ 0 , 8 , 10 , 6 , 3 , 7 , 2 ] value=[0,8,10,6,3,7,2] value=[0,8,10,6,3,7,2]
从而可以得到 d p [ i ] [ j ] dp[i][j] dp[i][j] 的边界条件。即:
d p [ i ] [ 0 ] = 0 ( i = 0 , 1 , 2... , 6 ) dp[i][0]=0 \quad (i=0,1,2...,6) dp[i][0]=0(i=0,1,2...,6)
d p [ 0 ] [ j ] = 0 ( j = 0 , 1 , 2... , 12 ) dp[0][j]=0 \quad (j=0,1,2...,12) dp[0][j]=0(j=0,1,2...,12)
代码实现如下:

// 0 1 背包问题
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
	vector<int> weight = { 0,4,6,2,2,5,1 };         //物品的重量(补零后)
	vector<int> value  = { 0,8,10,6,3,7,2 };        //物品的价值(补零后)
	int bearing = 12;                                       //背包的承重
	int n = weight.size();                                  //物品数量(补零后)
	vector<vector<int>> dp(n, vector<int>(bearing + 1));    //状态转移数组

	//核心代码
	for (int i = 1; i < n; i++)
	{
		for (int j = 1; j < = bearing; j++)
		{
			if (j < weight[i])
				dp[i][j] = dp[i - 1][j];
			else
				dp[i][j] = max(dp[i - 1][j - weight[i]] + value[i], dp[i - 1][j]);
		}
	}

	cout << "所装物品最大价值:" << dp[n - 1][bearing];
	return 0;
}

结果为24.
如果将 d p [ i ] [ j ] dp[i][j] dp[i][j] 全部打印出来,则如下表所示:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值