浅谈背包dp

1. 01背包

1.1题面

以一维为例:有\(n\)件物品和一个容量为\(V\)的背包,放入第\(i\)件物品会占用\(c_i\)的容量/耗费为\(c_i\)的费用,同时也会得到值为\(w_i\)的价值。
求解将哪些物品放入后/装满背包时获得的价值最大?

1.2基本思路

由于dp问题的无后效性,所以我们就可以在第\(i\)个阶段只考虑第\(i\)件物品的最优决策
又因为背包的容量限制,所以我们需要一个二维数组来储存最优解:\(F[i,v]\)
其中\(i\)表示已完成对前\(i\)个阶段的决策(或正处于第\(i\)个阶段)
\(v\)表示在最多占用\(v\)的容量下的最优解

1.2.1 拆分子问题

对于01背包,阶段就是第\(i\)件物品,决策就是放与不放这第\(i\)件物品。

  1. 放第\(i\)件物品
    此时就要求\(v\ge c_i\)
    目前的最优解也就成了“前\(i-1\)件物品放进容量为\(v-c_i\)的背包”的最优解加上\(w_i\),\(F[i-1,v-c_i]+w_i\)
  2. 不放第\(i\)件物品
    此时背包中物品总价值不变,\(F[i-1,v-c_i]\)

1.2.2 实现状态转移

显而易见,求解目标是\(F[n,V]\)

因此外层循环就应控制阶段,内层循环就应控制背包容量\(v\)
为了满足\(v\ge c_i\)\(v\le V\)这两个条件,内层循环的循环变量\(v\)就应从\(c_i\)自增到\(V\)(对于\(c_i>v\)的情况,完全可以因为不满足循环条件而排除掉)

1.3 状态转移方程

\(F[i,v]=max\{F[i-1,v],f[i-1,v-c_i]+w_i\}\)

状态转移实现

for(int i = 1; i <= n; i++)
	for(int v = c[i]; v <= V; v++)
		F[i,v] = max(F[i-1,v], F[i-1, v - c[i]] + w[i]);

1.4 优化空间复杂度

以上算法的时间与空间复杂度均为\(\varTheta(Vn)\),显然时间复杂度无法优化,但空间复杂度可以优化至\(\varTheta(V)\)

1.4.1 优化思路

由于背包是求解不超过容量限制的情况下的最大价值,所以优化后的一维数组应是\(F[0...V]\)
每个\(F[i,v]\)都由\(F[i-1,v]\)\(F[i-1,v-c_i]\)递推而来,所以只需要保证在第\(i\)个阶段求解\(F[v]\)\(F[v]\)\(F[v-C_i]\)的值是第\(i-1\)个阶段求解得来的即可。
因此内层循环顺序就应是由\(V\)递减到\(c_i\)

优化实现

for(int i = 1; i <= n; i++)
	for(int v = V; v >= c[i]; v--)
		F[i,v] = max(F[v], F[v - c[i]] + w[i]);

1.5 不同题目的求解

在不同题目的求解过程中,差异存在于初始化\(F\)数组的部分
要搞清楚区别,首先要明白:初始化背包实际上就是将背包赋一个什么物品都不放时的合法状态

1.5.1 不超过容量限制时求最优解

因为要求不超过容量限制,所以任何一个符合\(0 \le v \le V\)的背包\(F[v]\)都有一个合法解(非最优),为不放任何物品。
因此在这种题目中可以直接将\(F\)数组全部初始化为\(0\)

1.5.2 恰好装满背包时求最优解

因为要求装满背包且每件物品的大小均不为\(0\),所以仅有\(F[0]\)存在一个这样的合法状态,因此将其赋为\(0\)
而其他容量的背包都不存在这样的一个合法情况,因此将其赋为\(-\infty\)

1.6 特殊的01背包问题

1.6.1 题面

以一维为例:有\(n\)件物品和一个容量为\(V\)的背包,放入第\(i\)件物品会占用\(c_i\)的容量/耗费为\(c_i\)的费用
求解:恰好将背包装满的方案数

1.6.2 基本思路

同上,先用一个\(F[i,v]\)求解,因为求解目标是方案数,所以\(F[i,v]\)表示的就是前\(i\)件物品中选取一部分并装满容量为\(v\)的背包的总方案数,求解目标也就是\(F[n,V]\)

1.6.3 与普通01背包的不同

1.6.3.1 状态转移方程

因为求解目标是方案总数,所以应该采用累加方式,并且内层循环应从\(1\)开始。
每一个阶段都有两种选择,放与不放。
对于每种可能的选择都要累加到最后结果中。
实现如下:

for(int i = 1; i <= n; i++)
	for(int v = 1; v <= V; v++)
	{
		F[i][v] += F[i-1][v];//不放的可能
		if(v >= c[i])	//能装下
			F[i][v] += F[i-1][v - c[i]]; //放的可能
	}

1.6.3.2 初始化

对于部分情况即当\(c[i]=v\)时,方案数会额外多一种:放入物品\(i\)恰好装满容量为\(c[i]\)的背包, 这个方案会在F[i][v] += F[i-1][v - c[i]]中被算入,因此就要求\(F[i,0]=1\),而不符合这个要求的物品,即使将\(F[i,0]\)赋为\(1\),也不会被计算,所以完全可以将\(F[0...n][0]\)都赋为\(1\)

posted @ 2025-05-09 19:13  Kelojonle  阅读(24)  评论(0)    收藏  举报