背包问题整理 (背包九讲)

本文详细介绍了背包问题的几种类型,包括01背包、完全背包、多重背包和混合背包,并探讨了解决这些问题的动态规划策略。通过二维和一维动态规划优化空间复杂度,以及针对不同限制条件如物品数量无限或有限进行策略调整。此外,还提及了二维费用的背包问题和分组背包问题,以及如何寻找解决方案的方案数。
摘要由CSDN通过智能技术生成

1. 01 背包问题

分析 : 

1. 二维动态规划:

f[i][j] = v 

-> 用前 i 件物品,使用容量为 j 的情况的 能获得的最大价值

转移方程:

对于 物品 i 来说

有取和不取两种方案:

然后两者取较大值即可

(a). 取:

f[i][j] = f[i-1][j-wi] + vi;

(b). 不取:

f[i][j] = f[i-1][j]

初始化:

f[0][0] = 0;

答案:

max{ f[i][] }

优化:

从上面的二维动态规划可以看出来,f[i] 之和 f[i-1] 的状态有关

所以可以用滚动数组的方式来优化空间复杂度

2. 一维动态规划

和上述的思路类似,对于遍历到的每一个 i 物品 ,f[j] 表示在使用 [0:i] 物品的情况下,容量限制 为 j 的情况下所能获得的最大价值

相当于在计算 f[i][j] = f[i-1][j - wi] + vi  要转变为 f[j] = f[j - wi] + vi

相当对在计算 f[j] 的时候比 f[j] 小的应该还未计算,所以遍历的时候从容量大到小遍历即可

***

此处不需要计算 max{f[]}

f[m] 就是答案 

因为此时是全部的 f[i] 都被初始化为 0

设 能获得的最大价值 使用容量是 k

相当于从 f[0] 加入这些值 转移到了 f[k] = max_v

也相当于可以从 f[m-k] 加入这些值 转移到了 f[m] = max_v

***

2. 完全背包

和 01 问题的区别:

每一件物品可以选择 无限件

一维做法:

f[i] -> 总体积为 i 的情况下,能获得的最大价值

res = { f[] }

正常情况下:

按照 01 背包的思路:

for(int i = 1;i <= n;i++) {
	// 对于物品的遍历
	for(int j = m;j >= w[i];j--) {
		// 对于可用体积的遍历
		for(int k = 1;k*w[i] >= j;k++) {
			// 此时放入多少个 物品 i
			f[j] = max(f[j],f[j - k * w[i]] + k * v[i]);
			// f[j - k * w[i]] 都是没有计算过的,上层的计算结果
			// 所以不会影响到该层计算
		}
	}
}

但是也可以不用 k 的那层循环,只需要改变 j 的遍历

for(int i = 1;i <= n;i++) {
	// 对于物品的遍历
	for(int j = w[i];j <= m;j++) {
		f[j] = max(f[j],f[j - w[i]] + v[i]);
        // 因为 我们此时不用关心 f[j - w[i]] 是否和物品i有关
        // 因为 物品 i 是可以无限制加的, 就算 f[j - w[i]] 中已有了 物品 i 也没有关系
	}
}
// 此时对于答案也就是 f[m] 同理于 上面 01 背包的情况

3. 多重背包问题

相比较于上述的完全背包:

多了每件物品的最多使用数量

解法 1: 

使用三重循环即可

第一个循环物品

第二个循环当前体积

第三个循环每件物品的个数

限制 : 当 n m s <= 100 时,否则会超时

解法 2:

二进制优化方法

当 n m s <= 2000 时

用上述的方法就会超时了

所以可以用二进制的方法来计算 :

比如某物品 i 的个数 s = 10

那么可以将物品 i 拆分成 4 份 每份分别有 1,2,4,3 件该物品 i 

这样就 退化为 01 背包问题 了 对于物品 i 的选取就变成了 4 份的 选与不选的问题,且相互独立

 

时间复杂度 也就变成了 : O(n * m * log(s) ) = O(2 * 10 ^ 7)

解法3:

单调队列优化 (*难度较大 暂省略*)

主要思路 :在三重循环中的 第三重循环 (对 物品 i 的个数 )  用单调队列来优化

4. 混合背包问题

思路 :就是对于每次放入的物品 i  进行判断 :

1. 对于 01 背包 的 物品 :

直接按照 01 背包 的遍历来进行 从大到小遍历 

2. 对于 完全背包 的 物品 :

直接按照 完全背包 的遍历来进行 从小到大遍历

3. 对于有个数限制 的 物品 :

按照二进制优化来分成 ceil(log(s)) 份 来进行

5. 二维费用的背包问题

 

思路 : 

f[i][j] -> 体积为 i,重量为 j 的情况下最大的价值是多少

每个物品只能用一次,则 从大到小 遍历即可

6. 分组背包问题 :

思路:

遍历每组 和其中的物品

7.  背包问题求方案数

按照 01 背包 的思路:

f[i] = x; 体积恰好为 i 的情况下 能获得的最大价值为 x

再开一个 g[N] 来表示 :

g[i] 表示 : f[i] = x 的情况下 方案数 的大小

1. 来判断 对于当前的体积 判断 当前体积的最大价值是从上面的哪个方案转移来的

2,3. s 用于记录 当前体积的最大价值的方案数

若是从 f[j] 来的就加上 g[j]

若是从 f[j - v] 来的就加上 g[j - v]

最后更新下 g[j] 即可

4. 首先一次遍历来求最后的能获得的最大的价值 maxw

然后计算能得到最大价值的方案数

8.  求背包问题的方案

用一个二维数组来标记每一轮选择的转移结果

箭头处 : 

当 f[n][m] == f[n-1][m] 说明 第n件物品是不选择的

当 f[n][m] == f[n-1][m - vn] + wn 说明 第n件物品是选择了

9. 有依赖的背包问题

暂略,后面遇到再补充

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值