问题描述

有NN件物品和一个容量为VV的背包.放入第ii件物品.放入第ii件物品耗费的容量是CiC_i,所获得的价值是WiW_i.每件物品只有一个.求将哪些物品放入背包可使价值总和最大.

思考过程

一般来说求极值的问题可分为贪心,动态规划,以及遍历所有可能.在这三中方法中,动态规划是最常见的,也是很难想出来的.其中最难的是定义子问题,写出动态转移方程.类似于将问题简化,如何由较小的子问题,推到出较复杂的问题.类似的计算机的本质是极其简单的二进制运算,但由无数二进制运算叠加在一起,组成了今天复杂的互联网系统.

在上次面试中,由于没想出思路,很紧张,之后写最简单的01背包也没有写出来.面试后,痛定思痛,之后要逐步加强的自己的算法思维能力,证明问题能力,找错能力,工具应用能力.

思路

一般可从最简单入手:

  • 1如果没有物品,解是什么?显而易见,由于没有任何物体,得到的价值为0.这一般对应于动态规划中的初始化.
  • 2再复杂一点点,如果有一个物体,如何求解?直接比较物品体积和背包容量,如果小于,直接拿下,解就是物体的价值,否则还是0.
  • 3 当有两个物体时呢?如果先考虑第二个物体,那么有两种选择,装或者不装,两种选择之后,会产生新的容量,以及第一个物体,那么问题回归到第二种情况.
  • 如果有NN个物体,如果放完第NN个物体(选择放或者不放)后,那么问题转化为含有N−1N-1个物体的子问题.这样问题就逐渐分解了.

定义子问题

用F[i,v]F[i, v]表示前ii件物品放入一个容量为vv的背包所获得的最大价值.

状态转移方程

在放与不放两个子问题中选择一个最大的解

F[i,v]=max(F[i−1,v],F[i−1,v−Ci]+Wi)

F[i, v] = max(F[i - 1, v], F[i - 1, v - C_i] + W_i)

伪代码

F[0, 0...V] = 0
for i = 1 to N for j = 0 to Vif(C[i] > j)F[i, j] = F[i - 1, j]elseF[i, j] = max(F[i - 1, j], F[i - 1, j - C[i]] + W[i])

优化

时间

时间复杂度已无法再优化,.

T=O(NV)

T = O(NV)

空间

这里使用的是二维数据,但求解第ii行值时,只利用到i−1i - 1行的解,并未利用更久之前的解,由于有这种局限解的特性,可以利用一维的滚动数组模拟二维数据,另外由于这里由i−1i-1的前面的解j−c[i]j - c[i]推导出ii后面的解jj,也就是利用一维的数据的话,旧解为前面的解,新解为后面的.那么就应该让jj从大到小进行循环遍历,因为这样第一次接触的到为旧解i−1i-1,新出来的新解jj在此次遍历也不会再用到.也就是当且仅使用了一次.如果是从小到大遍历jj,那么新求出的解j+C[i]j + C[i]在后面还会遍历到,因此还会被重新用到,再次用到的话,还会选择是否拿下这个物体,不符合题意每件物品只有一个(如果有无穷多个物品,就可以从小到大遍历,之后的完全背包问题会提到)

S=O(V)

S = O(V)

优化空间后的伪代码 1

F[0...V] = 0
for i = 1 to Nfor j = V to C[i]F[j] = max(F[j], F[j - C[i]] + W[i]

代码更将简单优雅

初始化的细节

如果是求恰好要装满背包时的最优解,那么像上面的都初始化为0就不行了.这时候需要只有F[0, 0] = 0, F[0, 1…V] = INT_MIN(表示负无穷).可以理解只有0个物品,背包为0容量正好有解,其余解为无效解.如果手工画出来子问题转化图示,这样最基础的子问题只能从F[0,0]F[0,0]出发,不会从其他无效子问题出发,因为初始化无效解是负无穷,两者去较大的话,肯定不会选取负无穷.

一个常数的优化

可将伪代码1再次优化为

for i = 1 to Nfor j = V to max(V - sum(C[i...N]), C[i])F[j] = max(F[j], F[j - C[i]] + W[i])

由于只需要最后F[V]的值,倒推前一个物品,其实只要知道F[V-C[N]]即可。以此类推,对以第j个背包,其实只需要知道到F[v-sum{C[j…n]}]即可

求取最优方案

参照一般动态规划问题输出方案的方法:记录下每个状态的最优质是由状态转移方程的哪一项得到的,也就是记录最优解时,是否选择了这个物品.具体来说是利用path[i, j] 来记录当前的转移过程,path[i, j] = true表示选取物品i, path = False 表示未选取当前物品.(bool数组更加节省内存).空间复杂度S=O(VN)S = O(VN).

代码库(C++)

不输出最优方案

void zeroOnePack(int c, int w, int V, vector<int> & F){for(int i = V; i >= c; i--)F[i] = max(F[i], F[i - c] + w);
}
int allZeroOnePack(vector<int>& C, vector<int>& W, int V){vector<int> F(V + 1, 0);for(int i = 0; i < C.size(); i++)zeroOnePack(C[i], W[i], V, F);return F[V];
}

输出最优方案

void zeroOnePack(int i, int c, int w, int V, vector<int> & F, vector<vector<bool> > &path){for(int j = V; j >= c; j--){int choose = F[j - c] + w;if(choose > F[j]){F[j] = choose;path[i][j] = true;}}
}
int allZeroOnePackDetail(vector<int>& C, vector<int>& W, int V){vector<int> F(V + 1, 0);vector<vector<bool> > path(C.size(), vector<bool>(V + 1, false));for(int i = 0; i < C.size(); i++){zeroOnePack(i, C[i], W[i], V, F, path);}return F[V];
}
void printMethod(vector<int>& C){int i = N - 1;int v = V;while(i >= 0){if(path[i][v]){cout<<i<<" ";v = v - C[i];}i--;}
}

另一个小的常数时间优化

在利用dfs递归求解时,先将物品按照单位价格排好序,单位价格高的靠前,这样如果某个物品超载时,没必要再累积其后面物品的价格, 而是按照该物品的单位价格乘以剩余容量,这样算出的总价格虽然比实际装载的总价格略高些,如果这样略高于实际值的解还低于当前的最优解,则可对后面剪枝,避免多余的计算.

Reference

  1. 背包九讲
  2. 0-1背包问题(回溯结点类排序改进)
  3. 01背包问题:Charm Bracelet (POJ 3624)(外加一个常数的优化)

背包问题九讲笔记-01背包问题相关推荐

  1. 背包九讲之一:01背包问题

    文章目录 说明: 01背包问题 题目 基本思路 初始化的细节问题 优化空间复杂度 相关题目练习 题目URL 输入格式 输出格式 数据范围 输入样例 输出样例: 题目解法 说明: 本文所讲内容摘录自崔添 ...

  2. 背包问题九讲_背包问题

    背包问题九讲 我发现背包问题既棘手又有趣. 我敢肯定,如果您正在访问此页面,您已经知道了问题说明,但是只是为了完成本章: 问题: 给定一个最大容量为W和N的背包,每个背包都有自己的值和重量,将它们放入 ...

  3. 背包问题九讲 v1.0

    背包问题九讲 v1.0 目录 第一讲 01背包问题 第二讲 完全背包问题 第三讲 多重背包问题 第四讲 混合三种背包问题 第五讲 二维费用的背包问题 第六讲 分组的背包问题 第七讲 有依赖的背包问题 ...

  4. [转载学习] 背包问题九讲

    背包问题九讲 v1.0 目录 第一讲 01背包问题 第二讲 完全背包问题 第三讲 多重背包问题 第四讲 混合三种背包问题 第五讲 二维费用的背包问题 第六讲 分组的背包问题 第七讲 有依赖的背包问题 ...

  5. 背包问题九讲学习小记

    前言 有些大佬小学就啃完背包问题九讲了,%%%%%. 细节落实要细致. 原目录(大致意思) 1 01背包 2完全背包 3多重背包 4 123讲的综合 5二维费用的背包问题 6分组背包 7依赖性背包 8 ...

  6. 01背包问题分支限界java_分支限界法-01背包问题

    1.分支限界法介绍 分支限界法类似于回溯法,也是在问题的解空间上搜索问题解的算法.一般情况下,分支限界法与回溯法的求解目标不同.回溯法的求解目标是找出解空间中满足约束条件的所有解:而分支限界法的求解目 ...

  7. c语言 用回溯算法解决01背包问题,回溯法解决01背包问题

    <回溯法解决01背包问题>由会员分享,可在线阅读,更多相关<回溯法解决01背包问题(21页珍藏版)>请在人人文库网上搜索. 1.回溯法解决01背包问题,回溯法解决01背包问题, ...

  8. python 完全背包问题_背包问题九讲python3实现

    背包九讲是动态规划思想的经典呈现,找了许久没有完整的python3实现,趁机总结一下. 1.0-1背包问题 二维DP数组解法: # n, v分别代表物品数量,背包容积 n, v = map(int, ...

  9. 1008-----算法笔记----------0-1背包问题(动态规划求解)

    1.问题描述 给定n种物品和一个背包,物品i的重量是wi,其价值为vi,背包的容量为C.问:应该如何选择装入背包的物品,使得装入背包中物品的总价值最大? 2.问题分析 上述问题可以抽象为一个整数规划问 ...

  10. 动态规划——背包问题九解(01背包)

    01背包问题 问题 有 N 件物品和一个容量是 V 的背包.每件物品只能使用一次. 第 i 件物品的体积是 vi,价值是 wi. 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最 ...

最新文章

  1. 局部内部类访问方法中的局部变量为什么加final
  2. 在cisco路由器上实现DHCP功能实例
  3. 关于xib里面的NSLayoutConstraint的multiplier修改
  4. SpringCloud(第 057 篇)CentOS7 安装 maven 编译工具
  5. docker安装教程(windows和linux[centos8])
  6. Choose the best route
  7. 我的世界java版记分板_我的世界计分板教程 计分板指令详解
  8. 设计模式---适配器模式(转自可均可可博客)
  9. lede 自定义linux,OpenWrt 和 LEDE 宣布正式合并
  10. python2安装包_解决win10下python2和python3共存问题
  11. 论文笔记_S2D.31_2015-CVPR_对单张图像进行统一的深度和语义预测
  12. 黑苹果hidp显示不清楚_bigsur 黑苹果开启HIDPI失败解决方法
  13. 今日分享:js制作一个简单的新年倒计时
  14. tf.nn.tanh 双曲正切曲线
  15. 【PS】106个水彩花卉和树叶画笔
  16. 机器学习 主成分分析(Principal Component Analysis)
  17. android下雨动画效果,Android利用SurfaceView实现下雨的天气动画效果
  18. 新春蓝牙耳机怎么选?五年发烧友吐血盘点,高性能蓝牙耳机推荐
  19. HDU 4313 最小生成树
  20. cfg80211 subsystem中的wiphy

热门文章

  1. Python爬虫入门教程24:下载某网站付费文档保存PDF
  2. android 桌面小插件下载地址,桌面小组件app下载
  3. C语言学习7:ASCII码表及用法简介
  4. 浏览器大战之谷歌浏览器的逆袭
  5. python wget_python wget下载文件
  6. 阿里云短信SDK使用
  7. 算法——排序——插入排序图解动画
  8. 基于EEG信号的睡眠分期算法记录2-一种新的全自动随机森林睡眠分期算法(英)
  9. 大津阈值分割(OSTU)
  10. 微型计算机相关的英文文献,微型计算机控制系统(单片机控制系统)外文文献翻译.doc...