网站首页 > 资源文章 正文
01背包问题解法
原创: Jiau 机器感知 8月7日
未经许可,禁止转载
1. 定义
我们有$N$种物品,物品$i$的重量为$w[i]$,价格为$p[i]$。我们假定所有物品的重量和价格都是非负的,背包所能承受的最大重量W,如果限定每种物品只能选择0个或1个,则问题称为0-1背包问题 。
2. 二维动态规划解法
二维动态规划其实就是填表,表格中的值表示能获得的最大总价值,表格如下:
图片来源于国外的一个网站,网站地址见附录,感兴趣的可以去看看。
现在我们分析动态规划中的状态转移,假设当前待求状态为table[i][j],其中,$i$表示第$i$个物品,$j$表示当前背包容量,由于是01背包问题,即一个物品只能选或者不选,也即当前最优解的可能有两种,即选与不选,对应的总价值分别为:table[i-1][j]、table[i-1][j-w[i]]+v[i],解释如下:
当不选择第$i$件商品时,当前最大价值就和状态$i-1$,容量为$j$时的最优解一样;当选择第$i$件物品时,当前最大价值就状态$i-1$,但容量为$j-w[i]$,再加上当前物品价值$v[i]$时的价值,由此可得,该问题的状态转移方程如下:
table[i][j] = max(table[i-1][j], table[i-1][j-w[i]]+v[i])
有了这个状态转移方程之后,我们就可以写程序了,示例程序如下:
// total weight
#define W (18)
// total stuff
#define N (7)
int table[N+1][W+1] = {0};
int value[N+1] = {0, 12, 10, 8, 11, 14, 7, 9};
int weight[N+1] = {0, 4, 6, 5, 7, 3, 1, 6};
void knapsack()
{
int k, w;
for (k=1; k<=N; k++) {
for (w=1; w<=W; w++) {
if (weight[k] > w) {
table[k][w] = table[k-1][w];
} else {
int value1 = table[k-1][w-weight[k]] + value[k];
int value2 = table[k-1][w];
if (value1 > value2) {
table[k][w] = value1;
} else {
table[k][w] = value2;
}
}
}
}
}
运行结果如下,对比网图可知,结果完全相同:
3. 最优解回溯
通过上面的方法可以求出背包问题的最优解,但还不知道这个最优解由哪些商品组成,故要根据最优解回溯找出解的组成,根据填表格时的规则,反推可知:
a. 如果table[i][j] == table[i-1][j],这说明第$i$件商品没有被选择,则上一步就是在table[i-1][j]的位置处;
b. 如果table[i][j] != table[i-1][j],说明选择了第$i$件商品,再根据table[i][j] = table[i-1][j-w[i]] + v[i] 可得到上一个位置为table[i-1][j-w[i]]位置处;
c. 如此反复找到 i=0 为止。
示例代码如下:
void show_track()
{
vector<pair<int, int>> track;
int i=N, j=W;
while (i>0) {
if (table[i][j] == table[i-1][j]) {
i--;
} else {
track.push_back(make_pair(i, j));
int w = weight[i];
j -= w;
i--;
}
}
for (int i=0; i<track.size(); i++) {
cout << track[i].first << ' ';
}
}
4. 复杂度分析
根据程序分析可知,我们需要填的表格数量为$N*W$,因此时间复杂度和空间复杂度都是$O(N*W)$,因为要遍历完所有的可能性才可能得到最优解,而不像有序数组一样可以二分法或者什么的,所以时间复杂度没法优化了,但空间复杂度还是可以优化的,因为我们每次更新只与上一时刻状态有关,所以不需要保留这么多时刻的状态,空间复杂度可以优化到$O(W)$,首先分析上一步的核心代码:
for (w=1; w<=W; w++) {
for (k=1; k<=N; k++) {
if (weight[k] > w) {
table[k][w] = table[k-1][w];
} else {
int value1 = table[k-1][w-weight[k]] + value[k];
int value2 = table[k-1][w];
......
}
可以看出,核心代码部分,每次更新仅与上一时刻状态有关,对应到table中就是,每次更新只与table的一个行数据有关,这也就是空间复杂度为什么能够优化为$O(W)$的原因,如果简单的优化成如下代码:
for (w=1; w<=W; w++) {
for (k=1; k<=N; k++) {
if (weight[k] > w) {
table[w] = table[w];
} else {
// 需要 w 前方的 w-weight[k] 处的值
int value1 = table[w-weight[k] + value[k];
int value2 = table[w];
table[w] = ...;
}
上述代码不会得到正确的结果,为什么呢?行扫描我们是从左到右进行的,而我们需要取 $w-weight[k]$,即$w$左边的值,而我们却从左边开始更新的,那么就出现了我们需要的值被开始的更新覆盖了,得到的就是当前时刻的值了,不是期待的上一时刻的值,很显然不可能的得到正确的结果了,为了避免这种覆盖现象发生,我们可以通过反向扫描更新,即从右到左更新,这样更新的位置确定不会被左方的需要,就可以得到正确的结果了,代码如下:
void knapsack_optimization()
{
// 仅使用 table 中第一行
int k, w;
for (k=1; k<=N; k++) {
for (w=W; w>=1; w--) {
if (weight[k] > w) {
table[0][w] = table[0][w];
} else {
int value1 = table[0][w-weight[k]] + value[k];
int value2 = table[0][w];
if (value1 > value2) {
table[0][w] = value1;
} else {
table[0][w] = value2;
}
}
}
}
}
5. 总结
01背包问题解法使用的是二维动态规划进行求解的,朴素二维的动态规划时间复杂度和空间复杂度都是$O(N*W)$,而经过分析代码可以看出,状态转移时,当前时刻状态只与上一时刻的状态有关,所以空间复杂度可以优化到$O(W)$,在优化代码是需要注意的是:需要改变行更新顺序,为了避免新的状态覆盖上一时刻的状态,需要从右到左进行更新状态;有了最优解之后就是回溯最优解,方法步骤遵循上述步骤即可。
附录
http://karaffeltut.com/NEWKaraffeltutCom/Knapsack/knapsack.html
- 上一篇: 面试高频算法系列 | 第1话-01背包
- 下一篇: 01背包问题的js解决方式(背包问题csdn)
猜你喜欢
- 2024-10-15 【枫叶节徒步活动】12月01日第一届清远16公里暨田野绿世界枫叶节
- 2024-10-15 国货怎么样?自由兵战术信使包终极测评!
- 2024-10-15 看多了lv supreme等品牌我们来看看Evisu福神背包
- 2024-10-15 河北省宁晋县收藏的二战时期常见的侵华日军几种背包
- 2024-10-15 大师级背包,搭载HAR MK01和自改背包的白狗
- 2024-10-15 未行 FF01 三明治变形背包(未行+ff01+三明治变形背包是什么)
- 2024-10-15 猜猜耶伦手提包里是什么?斜背包里放的又是什么?为什么一定要自
- 2024-10-15 拆了GP01高达背包,宇宙突击式样龟霸
- 2024-10-15 Python算法题解:动态规划解0-1背包问题
- 2024-10-15 王者编程大赛之三—最大价值(01背包)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- 电脑显示器花屏 (79)
- 403 forbidden (65)
- linux怎么查看系统版本 (54)
- 补码运算 (63)
- 缓存服务器 (61)
- 定时重启 (59)
- plsql developer (73)
- 对话框打开时命令无法执行 (61)
- excel数据透视表 (72)
- oracle认证 (56)
- 网页不能复制 (84)
- photoshop外挂滤镜 (58)
- 网页无法复制粘贴 (55)
- vmware workstation 7 1 3 (78)
- jdk 64位下载 (65)
- phpstudy 2013 (66)
- 卡通形象生成 (55)
- psd模板免费下载 (67)
- shift (58)
- localhost打不开 (58)
- 检测代理服务器设置 (55)
- frequency (66)
- indesign教程 (55)
- 运行命令大全 (61)
- ping exe (64)
本文暂时没有评论,来添加一个吧(●'◡'●)