#include <iostream> using namespace std; //动态规划:0-1背包问题 //bestValue[i][j]=max ( bestValue[i+1][j-w[i]]+v[i] ,bestValue[i+1][j] ) w[i]<=j //bestValue[i][j]=bestValue[i+1][j] w[i]>j class Knapsack { private: int *weight;//物品重量数组 int *value;//物品价值数组 int numOfItems;//物品数量 int bagSpace;//背包容量 int **bestValue;//动态规划表格,记录bestValue[i][j]的价值,为最优价值,i表示物品i…n装入容量为j的背包能达到的最大价值 int **path;//为了求出取得最优值时的解,记录动态规划表不同表项的选择与否 public: //构造函数 Knapsack(int numOfItems,int bagSpace) { weight=new int[numOfItems+1]; value=new int[numOfItems+1]; this->bagSpace=bagSpace; this->numOfItems=numOfItems; bestValue=new int* [numOfItems+1]; for(int i=0;i<numOfItems+1;i++) { bestValue[i]=new int[bagSpace+1]; } path=new int* [numOfItems+1]; for(int i=0;i<numOfItems+1;i++) { path[i]=new int[bagSpace+1]; } } //输入物品的重量与价值 void input() { int i=1; while(i<=numOfItems) { cout<<“输入第”<<i<<“个物品的重量”<<endl; cin>>weight[i]; cout<<“输入第”<<i<<“个物品的价值”<<endl; cin>>value[i]; ++i; } } //动态规划核心算法 void knapsack() { //初始化递归最底层,即将bestValue[n][0:c]进行初始化 for(int i=0;i<=bagSpace;i++) { if(weight[numOfItems]<=i) { bestValue[numOfItems][i]=value[numOfItems]; path[numOfItems][i]=1; } else { bestValue[numOfItems][i]=0; path[numOfItems][i]=0; } } //递推的进行动态规划,自底向上,最终bestValue[1][bageSpace]为1-n物品放入容量bagSpace内的最大价值 for(int k=numOfItems-1;k>=1;k–) { for(int j=0;j<=bagSpace;j++) { bestValue[k][j]=bestValue[k+1][j]; path[k][j]=0;//不放入的情况 if(weight[k]<=j)//如果容量足够放入当前物品 { if(bestValue[k+1][j-weight[k]]+value[k]>bestValue[k][j])//如果放入的价值大于不放的价值 { bestValue[k][j]=bestValue[k+1][j-weight[k]]+value[k]; path[k][j]=1;//那么就选择放入 } } } } } //输出最大价值,并且输出选择方式 void display() { //打印出bestValue[1][bagSpace],表示1…numOfItems的物品装入容量为bagSpace的最大价值 int i=1; int j=bagSpace; cout<<“最大价值为”<<bestValue[1][j]<<endl; //根据path[1][bagSpace]的记录开始,递归到path[n][某容量],从而打印出每个物品是否被选择进入背包 while(i<=numOfItems) { if(path[i][j]==0)//如果i物品没被放入,看i+1个物品装入容量j背包 { ++i; } else { cout<<“<重量:”<<weight[i]<<“,价值:”<<value[i]<<“>”endl; j-=weight[i]; ++i; } } } }; void main() { Knapsack test(5,50);//5个物品,背包容量50 test.input();//输入5个物品的价值与重量 test.knapsack();//动态规划 test.display();//打印选择与最大价值 }
思路总结: 看到一个题目,首先看问什么,下面以此题举例分析一下。
0-1背包问题
1,问题要求什么?
答:求把n个物品放入容量C的背包内能达到的最大价值
2,转换成一个抽象一点的数学表达式是什么?
答:bestValue[n][C],表示n个物品放入容量C的背包的最大价值
3,不考虑算法应该怎么选择,我们实际去解决这个问题的时候,是从哪里开始去做的?
答:我们有n个物品,C容量背包。 于是我们开始解决问题,我先放第一个物品,如果能放进去,我就放进去,当然,我也可以不放。
第一个物品处理结束以后,我们着手于第二个物品,能放进去就放进去,当然,我们也可以不放。
所以,这就是一个决策问题,决策是从我们实际处理问题中抽象出来的,我们放物品的时候只能一个一个放,决策是放或者不放。
4,在决策了解的情况,我们应该考虑当前要求的bestValue[n][C],在决策放入或者不放入的情况,分别等于什么?
答:如果能够放入,那么我们的背包还有C-w[i], 物品还有n-1个,当然,我们也可以选择不放进去,那么我们背包依旧有C容量,物品还有n-1个。 所以我们修改一下我们对bestValue[n][C]的定义,从而就得到了一个最优子结构的递归公式。
为了我们决策的进行,即我们每次决策都是最第i个物品进行决策,所以bestValue[n][C]修改为best[i][C],表示i,i+1,i+2…n个物品放入容量为C的背包的最大价值。
所以:bestValue[i][j]=max ( bestValue[i+1][j-w[i]]+v[i] ,bestValue[i+1][j] ) w[i]<=j
bestValue[i][j]=bestValue[i+1][j] w[i]>j
意思是:
如果当前容量j装不下物品i,那么i到n装入j的最大价值就等于i+1到n装入j的最大价值,就是公式的第二行。
如果当前容量j可以装下物品i,那么我们可以装进去,当然,也可以犯贱,不装进去,看看结果如何,所以i到n个物品装入j容量背包的最大价值就等于 i+1到n物品装入j-w[i]容量的背包可以达到的最大价值+value[i] ,i+1到n物品装入j容量背包的最大价值,这两种不同决策的一个最大值。
总结:解决什么? 从哪里开始做起? 有哪些决策? 决策后会怎么样?
找出了递归式,它具有最优子结构性质,即可以简单的理解为:当前的最优产生于子问题的最优,然后子问题的最优不受当前最优的影响,并且通过观察递归公式,应该找到递归的最底层的i,j分别是什么,我们观察到i在逐渐增加,j在逐渐减小,所以我们在递推的时候,首先把最底层进行初始化,然后利用递归公式向上递推。 所以我们需要首先初始化bestValue[n][0:C],即记录第n个物品装入0到C的背包的能达到的价值,当w[n]<=j时,bestValue[n][j]等于value[n],如果w[n]>j,即容量不够,那么就是0.
我们能够从底向上递推的重要原因就是:最优子结构+无后效性 。 多多体会吧。 这是基础理解了。