动态规划之背包问题
来源:互联网 发布:2016双十一大数据 编辑:程序博客网 时间:2024/06/18 06:51
01背包
1.问题描述
将N个物品,放入容量为C的背包之中,这N个物品的价值v与重量w都已知,求可以放入物品的最大价值。
2.算法思想
设:f[i][j] 为当容量为j,前i个物品备选时,背包可放入物品的最大值;w[i]为第i件物品的重量weight;v[i]为第i件物品的价值value。(i >= 0, w[0] = 0, v[0] = 0,即“第0件物品的重量、价值都为0”)
“将前i件物品放入容量为j的背包中”,这个子问题,若只考虑第i件物品放或者不放,那么就转化为只牵扯前i件物品的问题:
1)不放第i件物品,那么问题就转化为“前i-1件物品放入容量为j的包中” -> f[i-1][j]
2)放第i件物品,那么问题就转化为“前i-1件物品放入容量为j-w[i]的包中” -> f[i-1][j - w[i]] + v[i]
能放的条件是:第i件物品的重量w[i]小于等于此时背包的容量j。且,能放时,最大值为max(放,不放)。
3.数学表达
1) f[i][0] = f[0][j] = 0
2) f[i][j] = f[i-1][j],(w[i]>j)
3) f[i][j] = max{f[i-1][j],f[i-1][j-w[i]] + v[i]},(w[i]<=j)
4.java代码
public class DP {public static void main(String[] args) {DP dp = new DP();int[] weight = {0, 5 , 6, 4};int[] value = {0, 20, 10, 12};dp.solve(3, weight, value, 10);}void solve(int num, int[] weight, int[] value, int capacity) {int[][] f = new int[num + 1][capacity + 1];for (int i = 0; i <= num; i++) {for (int j = 0; j <= capacity; j++) {if(i == 0 || j == 0) {f[i][j] = 0;} else if (weight[i] <= j) {f[i][j] = Math.max(f[i -1][j], f[i - 1][j - weight[i]] + value[i]);} else {f[i][j] = f[i -1][j];}}}System.out.println(f[num][capacity]);}}
---------------------------------------------------------------------
5.优化
上面的方法是将每次外循环都计算出0~capacity个值存为二维数组的一行,因为计算下一行会用到上一行的值(f[i][j] = max{f[i-1][j],f[i-1][j-w[i]]+f[i]})。
由于一维数组也可以保存上一次循环的值,那么便可以将空间优化到O(capacity),这里需要保证在推f[i][j]时(也即在第i次外循环中推f[j]时)能够得到f[i-1][j]和f[i-1][j-w[i]]的值。若按照以前的循环方式,则在第i次循环中的f[i-1][j]就等于f[i][j],可以得到 (值还未计算并覆盖)。关键是f[i-1][j-w[i]]。
若w[i] >0,即对于j前面的那些,上一次循环的值就已经在本次循环被计算并覆盖了。故,不可行。
因此,按照《背包九讲》里(不是我想到的...),采用逆序的方式。想想逆序为何不会覆盖?因为j-w[i]啊,逆序的时候小于j的值未被覆盖呢!!
这里要好好想想,最好自己动手填表试试,顺序填的表和逆序填的表不一样的。
6.优化后的数学表达
1) i == 0 || j == 0时,f[j] = 0;
2) f[j] = f[j],(w[i]>j)
3) f[j] = max{f[j],f[j-w[i]] + v[i]},(w[i]<=j)
7.优化后代码
void solve2(int num, int[] weight, int[] value, int capacity) {int[] f = new int[capacity + 1];for (int i = 0; i <= num; i++) {for (int j = capacity; j >= 0; j--) {if (j >= weight[i]) {f[j] = Math.max(f[j], f[j - weight[i]] + value[i]);} else {f[j] = f[j];//嗯,是句废话,为了思路清楚起见写出来}}}System.out.println(f[capacity]);}
完全背包
有N种物品和容量为V的背包,每种物品都有无限件可用。第i种物品的重量是w(i),价值是v(i)。求解将哪些物品装入背包可使这些物品的总重量不超过背包,且价值总和最大。
直接用一维数组的方法:
for i=1..N for v=0..V f[v]=max{f[v],f[v-c[i]]+w[i]}
你会发现,这个伪代码与P01的伪代码只有v的循环次序不同而已。为什么这样一改就可行呢?首先想想为什么要按照v=V..0的逆序来循环。这是因为要保证第i次循环中的状态f[i][v]是由状态f[i-1][v-c[i]]递推而来。换句话说,这正是为了保证每件物品只选一次,保证在考虑“选入第i件物品”这件策略时,依据的是一个绝无已经选入第i件物品的子结果f[i-1][v-c[i]]。而现在完全背包的特点恰是每种物品可选无限件,所以在考虑“加选一件第i种物品”这种策略时,却正需要一个可能已选入第i种物品的子结果f[i][v-c[i]],所以就可以并且必须采用v= 0..V的顺序循环。这就是这个简单的程序为何成立的道理。
void solve3(int num, int[] weight, int[] value, int capacity) {int[] f = new int[capacity + 1];for (int i = 0; i <= num; i++) {for (int j = 0; j <= capacity; j++) {if (j >= weight[i]) {f[j] = Math.max(f[j], f[j - weight[i]] + value[i]);}}}System.out.println(f[capacity]);}
参考:
http://www.cnblogs.com/jbelial/articles/2116074.html
http://blog.csdn.net/ls5718/article/details/52227908
http://www.wutianqi.com/?p=539
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 动态规划之背包问题
- 简单数据库操作
- git中的 .gitignore 的忽略规则
- win7 访问其他服务器的docker
- python3 实现 A+B Problem(百练OJ:1000)
- win7桌面文件夹删不掉
- 动态规划之背包问题
- MySQL和Redis 数据同步解决方案
- Fragment页面进行日夜间模式切换
- oracle触发器
- java web学习总结39:数据库连接池
- idea的插件安装
- SSH综合项目实战(快递) -- day04 快递员分页查询、POI读取Excel、代码重构
- 这是一个记录一些技术细节给自己看的
- RAID磁盘阵列简介