背包问题理解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_25847123/article/details/86650038

申明:仅个人小记

前言

只讨论0-1背包完全背包问题,以游戏闯关为背景辅助理解背包问题。

一、基本交代

0-1背包问题

d p [ i ] [ j ] dp[i][j] 定义为体积为j的背包,从编号为 1,2,…i 的i个物品中挑选并放入背包,背包能盛放的最大价值。
状态转移方程
d p [ i ] [ j ] = m a x { d p [ i 1 ] [ j ] , d p [ i 1 ] [ j w ] + v } dp[i][j]=max\{dp[i-1][j],dp[i-1][j-w]+v\}
其中, w , v w,v 分别是编号为i的物品的重量(weight)和价值(value)。

完全背包问题

d p [ i ] [ j ] dp[i][j] 定义为体积为j的背包,从编号为 1,2,…,i 的i种物品中挑选并放入背包,背包能盛放的最大价值。
状态转移方程
d p [ i ] [ j ] = m a x { d p [ i 1 ] [ j ] , d p [ i ] [ j w ] + v } dp[i][j]=max\{dp[i-1][j],dp[i][j-w]+v\}
其中, w , v w,v 分别是编号为i的物品的重量和价值。

注意到: 0-1背包中每种物品只有一个,而完全背包每种物品的数量为无限个

二、逻辑部分

0-1背包问题

游戏闯关,每一关的编号为(i,j)。第(i,j)关中有编号为 1,2,…,i 的物品各一个,背包的体积为 j 。找出一个物品组合,使得物品能够放入背包并且背包中物品总价值达到最大,这个价值记为 d p [ i ] [ j ] dp[i][j] , 找到这个最大价值即可通关。
看的出来,这是一个组合问题,而且显然组合的情况很多(属于O(n!)级别)即计算复杂度很高。可以借助动态规划的思想,每次只考虑第(i, j)关中的第i个物品,而不关心其他的,进而大大降低复杂度。

开始分析

第 (i, j) 关中,我们只考虑第 i 个物品,对于第 i 个物品在最优解只有会被放入背包和不被放入背包两种情况

(1) 若第 i 个物品在最优解中

从空背包开始,第 i 个物品最终必然会在背包里面,我们可以直接最先将其放入空背包中。此时背包剩余体积为 j-w,物品还有1,2,…,i-1,我们需要从其中继续找出最好的组合使得在背包体积为j-w且物品为1,2,…,i-1的情况下达到价值最高(继续找的一定得是最好的组合吗?此处可以反证,即如果继续找的组合不是价值最高,那么加上已经放入背包的编号为 i 的物品,总体的价值并不是最高的,与最初假设最优矛盾)。
可以发现,这种局面等价于游戏的第 (i-1, j-w) 关,即体积为 j-w 的背包和1,2,…,i-1这些物品,求 d p [ i ] [ j w ] dp[i][j-w]
故而,此时可以确定出一个状态关系 d p [ i ] [ j ] = d p [ i 1 ] [ j w ] + v dp[i][j]=dp[i-1][j-w]+v

(2) 若第i个物品不在最优解中

显然,第 i 个物品最终不会出现在背包里面,故而,在第 (i,j )关游戏中,第 i 个物品存在或者不存都是一样的,并不会在最终结果中发挥作用,故而我们直接扔掉第 i 个物品。此时局面为背包体积为 j ,物品分别是1,2,…i-1这 i-1 个物品,我们要做的就是在这种局面下寻找最优解。
可以发现 ,这种局面等价于 游戏的第(i-1,j)关,即体积为 j 的背包和1,2,…,i-1 这些物品,求 d p [ i 1 ] [ j ] dp[i-1][j]
故而,此时确定出一个状态关系为 d p [ i ] [ j ] = d p [ i 1 ] [ j ] dp[i][j]=dp[i-1][j]

得出结论

到底第 i 个物品在不在最优解中,这得由我们的目标——最大价值化,来决定,所以,哪种情况下它的价值会更大,就选哪种。即 d p [ i ] [ j ] = m a x { d p [ i 1 ] [ j ] , d p [ i 1 ] [ j w ] + v } dp[i][j]=max\{dp[i-1][j],dp[i-1][j-w]+v\}

完全背包问题

第 (i,j) 关中,背包体积为 j, 物品种类编号为1,2,…,i,每种物品数量没有上限。此时,分使用 i 类物品和不使用 i 类物品两种情况。

开始分析

(1) 使用第 i 类物品

此时,因为第 i 类物品数量没有上限,而你只会使用有限量的第 i 类物品,所以,不管你用几个 i 类物品,当使用完后,物品的情况没有发生变化,即编号仍为1,2,…,i ,每种物品数量为无限。
此时至少使用1个第 i 类物品,这个数量是确定的,故而我们分析先用掉 1个第 i 类物品的情况: 此时背包剩余体积为 j-w, 物品情况没变,则此时的局面等价于游戏的第 (i, j-w) 关。
故而,确定出一个状态关系式为 d p [ i ] [ j ] = d p [ i ] [ j w ] + v dp[i][j] = dp[i][j-w]+v 注意:前后都是 i,而不是出现 i-1。

(2) 不使用第 i 类物品

显然,此时对于游戏的第(i,j)关来讲,第 i 类物品的存在是没有意义的,进而我们可以直接扔掉第 i 类物品,此时局面为: 背包体积为 j ,物品为编号1,2,…,i-1都是无限量,这个局面等价于游戏的第(i-1,j)关。此时,确定出一个状态关系为 d p [ i ] [ j ] = d p [ i 1 ] [ j ] dp[i][j] = dp[i-1][j]

得出结论

根据目标——背包盛放最大价值,我们来确定到底是否使用第 i 类物品,即 d p [ i ] [ j ] = m a x { d p [ i 1 ] [ j ] , d p [ i ] [ j w ] + v } dp[i][j]=max\{dp[i-1][j],dp[i][j-w]+v\}

三、代码优化

代码优化主要就是在空间复杂度上的优化,初步看上述状态关系式,dp 应该是用二维数组来实现,二维数组的规模为背包的体积值X物品的种类数目。仔细分析,发现 d p [ i ] [ j ] = m a x { d p [ i 1 ] [ j ] , d p [ i 1 ] [ j w ] + v } dp[i][j]=max\{dp[i-1][j],dp[i-1][j-w]+v\} d p [ i ] [ j ] = m a x { d p [ i 1 ] [ j ] , d p [ i ] [ j w ] + v } dp[i][j]=max\{dp[i-1][j],dp[i][j-w]+v\} 这两个中,每次计算一行时只会涉及对上一行或者本行前面部分的读取,基于这个现象,我们可以把二维数组优化为一维数组。优化后结果都是 d p [ j ] = m a x { d p [ j ] , d p [ j w ] + v } dp[j] = max\{dp[j],dp[j-w]+v\}
但要注意计算顺序是存在区别的。对于0-1背包问题,在计算 d p [ j ] dp[j] 的时候,应保证 d p [ j w ] dp[j-w] 没有被更新(没有更新意味着是上一行的数据),故而计算顺序应该 j 从大到小;对于完全背包问题,在计算 d p [ j ] dp[j] 时候,dp[j]要么直接来自上一行对应的一格的值 d p [ i 1 ] [ j ] dp[i-1][j] ,要么是来自本行的前面的值 d p [ i ] [ j w ] dp[i][j-w]

// list[i]为结构体对象,其中存放着第i类物品的单个物品的体积和价值
// 0-1背包计算顺序
for (int i = 1; i <= n; i++) // n种物品
	for (int j = s; j >= list[i].w; j --) // s 为指定的背包体积
		dp[j] = max(dp[j],dp[j-list[i].w]+list[i].v)
// 完全背包计算顺序
for (int i = 0; i <= n; i ++)
	for (int j = list[i].w; j <= s; j ++)
		dp[j] = max(dp[j],dp[j-list[i].w]+list[i].v)

猜你喜欢

转载自blog.csdn.net/qq_25847123/article/details/86650038
今日推荐