0-1背包问题用动态规划的递归实现与非递归实现
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
非递归实现:
view plain
1. 2. 3. 4. 5. 6. 7. 8. 9.
#include<iostream> using namespace std; const int W = 150; const int number = 5; const int VALUE[] = {60, 20, 10, 60, 100}; const int WEIGHT[] = {20, 30, 50, 60, 80}; int f[151];
递归实现:
view plain
1. #include<iostream> 2. using namespace std; 3. 4. const int W = 150;
5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24.
其中的f[v] = maxff[v]; f[v..c[i]]g一句恰就相当于我们的转移方程f[i][v] = maxff[i..1][v]; f[i..1][v .. c[i]]g,因为现在的f[v-c[i]]就相当于原来的f[i .. 1] [v .. c[i]]。如果将v的循环顺序从上面的逆 序改成顺序的话,那么则成了f[i][v]由f[i][v-c[i]]推知,与本题意不符,但 它却是另一个重要的背包问题P02最简捷的解决方案,故学习只用一维 数组解01背包问题是十分必要的。 注意这个过程里的处理与前面给出的伪代码有所不同。前面的示例程序 写成v=V..0是为了在程序中体现每个状态都按照方程求解了,避免不必 要的思维复杂度。而这里既然已经抽象成看作黑箱的过程了,就可以加 入优化。费用为cost的物品不会影响状态f[0..cost-1],这是显然的。
这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生 出来的。所以有必要将它详细解释一下:“将前i件物品放入容量为v的背 包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可 以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问 题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][v];如果 放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的 背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物 品获得的价值w[i]。优化空间复杂度以上方法的时间和空间复杂度均为 (V N),其中时间复杂度应该已经不能再优化了,但空间复杂度却可以 优化到(N)1。这个方程非常重要,基本上所有跟背包相关的问题的方程 都是由它衍生出来的。所以有必要将它详细解释一下:“将前i件物品放 入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不 放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i 件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值 为f[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下 的容量为v-c[i]的背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]再加上 通过放入第i件物品获得的价值w[i]。优化空间复杂度以上方法的时间和 空间复杂度均为(V N),其中时间复杂度应该已经不能再优化了,但空 间复杂度却可以优化到(N)。
10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33.
void ZeroOnePack(int w, int v) { for(int x = W; x >= w; x--) f[x]=(f[x] > (f[x-w]+v))?f[x]:(f[x-w]+v); }
这张图表刚好说明了调用ZeroOnePackage函数的整个过程,和 ZeroOnePackage函数里的执行for循环的执行过程:
c[i][j]数组保存了1,2,3号物品依次选择后的最大价值. 这个最大价值是怎么得来的呢?从背包容量为0开始,1号物品先 试,0,1,2,的容量都不能放.所以置0,背包容量为3则里面放4.这样,这一 排背包容量为4,5,6,....10的时候,最佳方案都是放4.假如1号物品放 入背包.则再看2号物品.当背包容量为3的时候,最佳方案还是上一排的 最价方案c为4.而背包容量为5的时候,则最佳方案为自己的重量5.背包 容量为7的时候,很显然是5加上一个值了。加谁??很显然是7-4=3的 时候.上一排 c3的最佳方案是4.所以。总的最佳方案是5+4为9.这样. 一排一排推下去。最右下放的数据就是最大的价值了。(注意第3排的背 包容量为7的时候,最佳方案不是本身的6.而是上一排的9.说明这时候3 号物品没有被选.选的是1,2号物品.所以得9.)
}
对于非递归的实现思路,我想下面这个例子和相应的图片是最好的说 明了: 因为背包最大容量M未知。所以,我们的程序要从1到M一个一个的 试。比如,开始任选N件物品的一个。看对应M的背包,能不能放进 去,如果能放进去,并且还有多的空间,则,多出来的空间里能放N-1 物品中的最大价值。怎么能保证总选择是最大价值呢?看下表。 测试数据: 10,3 3,4 4,5 5,6
if(j >= WEIGHT[i]) //背包剩余空间可以放下物品 i { r1 = Make(i-1,j - WEIGHT[i]) + VALUE[i]; //第i件物品放入 所能得到的价值 25. r2 = Make(i-1,j); //第i件物品不放所能得到的价值 26. r = (r1>r2)?r1:r2; 27. } 28. 29. return r; 30. } 31. 32. 33. void main() 34. { 35. int maxValue = Make(number-1, W); 36. cout<<"maxValue: "<<maxValue<<endl; 37. }
void main() { for (int i=0; i < 151; i++) { f[i] = 0; } for (int j=0; j < number; j++) { ZeroOnePack(WEIGHT[j], VALUE[j]); } cout<<"maxValue: "<<f[W]<<endl;
优化空间复杂度以上方法的时间和空间复杂度均为(V N),其中时间复 杂度应该已经不能再优化了,但空间复杂度却可以优化到(N)1。先考虑 上面讲的基本思路如何实现,肯定是有一个主循环i=1..N,每次算出来 二维数组f[i][0..V]的所有值。那么,如果只用一个数组f[0..V],能不能保 证第i次循环结束后f[v]中表示的就是我们定义的状态f[i][v]呢?f[i][v]是由 f[i-1][v]和f[i-1][v-c[i]]两个子问题递推而来,能否保证在推f[i][v]时(也即 在第i次主循环中推f[v]时)能够得到f[i-1][v]和f[i-1][v-c[i]]的值呢?事实 上,这要求在每次主循环中我们以v=V..0的顺序推f[v],这样才能保证推 f[v]时f[v-c[i]]保存的是状态f[i-1][v-c[i]]的值。 伪代码如下:
0-1背包问题的递归实现与非递归实现
分类: 算法2011-10-24 15:19 0人阅读 评论(0) 收藏 编辑 删除
题目有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是 w[i]。求解将哪些物品装入背包可使价值总和最大。基本思路这是最基 础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。用子 问题定义状态:即f[i][v]表示前i件物品恰放入一个容量来自百度文库v的背包可以获 得的最大价值。则其状态转移方程便是:
const int number = 5; const int VALUE[] = {60, 20, 10, 60, 100}; const int WEIGHT[] = {20, 30, 50, 60, 80}; //function Make( i {处理到第i件物品} , j{剩余的空间为 j}) :integer; int Make(int i, int j) { int r1 = 0; int r2 = 0; int r = 0; if (i == -1) { return 0; }