动态规划之0_1背包

来源:互联网 发布:tpo小站模考软件mac 编辑:程序博客网 时间:2024/05/30 23:00
问题描述:
   给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品,使得装
入背包中物品的总价值最大?


个人认为很重要的一点就是要理解    m[i][j]

对于m(i,j)就表示可选物品为i…n背包容量为j(总重量)时背包中所放物品的最大价值



对于问题的比较好的分析与解释见:http://blog.csdn.net/dapengbusi/article/details/7463968


下面是我的代码实现部分,如果你看懂了以上博主的问题分析和解释,理解就更加深刻了。



#include <iostream> using namespace std; #define MAX 20const int N = 5;void Knapsack(int v[],int w[],int c,int n,int m[][MAX]);void Traceback(int m[][MAX],int w[],int c,int n,int x[]);void printValue(int m[][MAX],int n,int c);int main(){int c=10;int v[]={0,6,3,5,4,3},w[]={0,2,2,6,5,4};//下标从1开始int x[N+1];int m[MAX][MAX];        cout<<"背包总容量:"<<c<<endl;cout<<"待装物品重量分别为:"<<endl;for(int i=1; i<=N; i++){cout<<w[i]<<" ";}cout<<endl;cout<<"待装物品价值分别为:"<<endl;for( i=1; i<=N; i++){cout<<v[i]<<" ";}cout<<endl;Knapsack(v,w,c,N,m);cout<<"背包能装的最大价值为:"<<m[1][c]<<endl;Traceback(m,w,c,N,x);cout<<"背包装下的物品编号为:"<<endl;for( i=1; i<=N; i++){if(x[i]==1){cout<<i<<" ";}}cout<<endl;        printValue(m,N,c);return 0;}void Knapsack(int v[],int w[],int c,int n,int m[][MAX]){int jMax = w[n]-1;//此处将jMax=w[n]-1即表示背包容量小于当前物品w[n]的情况int j,i;for( j=0; j<=jMax;j++)      //背包容量比当前w[n]的都小时,最底部的先初始化为0,比较下面可知,{                          //</span>之后同样的情况就是上一层继承下一层了。m[n][j]=0;}for( j=w[n]; j<=c; j++)    //最底部此时背包的最大总价值只能是当前这个物品的价值,而不管你背包{                          //</span>容量多大,因为只有 一个物品可以选择。m[n][j] = v[n];}for( i=n-1; i>1; i--){jMax = w[i]-1;   for( j=0; j<=jMax; j++)//同样是背包容量小于w[i],这里是该层继承下一层的值{m[i][j] = m[i+1][j];}for( j=w[i]; j<=c; j++) //此时有两种选择要么继承下一层的值,要么添加该层的值,{                        //所以要比较选取最大值得情况m[i][j] = (m[i+1][j]>m[i+1][j-w[i]]+v[i])?m[i+1][j]:m[i+1][j-w[i]]+v[i];}}//最后一次都不必循环了(因为循环的目的是为了利用中间值,在第一行所利用的中间值全都保存在了第二行,//所以此时只许判断总容量是否大于w[1],再选择是否加入背包中。m[1][c] = m[2][c];if(c>=w[1]){m[1][c] = (m[1][c]>m[2][c-w[1]]+v[1])?m[1][c]:m[2][c-w[1]]+v[1];}}//x[]数组存储对应物品0-1向量,0不装入背包,1表示装入背包void Traceback(int m[][MAX],int w[],int c,int n,int x[]){for(int i=1; i<n; i++){if(m[i][c] == m[i+1][c]){x[i]=0;}else{x[i]=1;c-=w[i];}}//由于存在上一层继承下一层的值(m[i][c] == m[i+1][c]),则当前层即当前编号为i的物品没有放入背包,//继续下一层,那么如果都到了底部的话,说明最底部的那个及编号为n的物品一定放入的背包中(因为        //肯定再无向下继承了)        //***只要判断到了m[n][c]不为0,那么最后一件物品必然放进了背包x[n]=(m[n][c])?1:0;     }void printValue(int m[][MAX],int n,int c){cout<<"打印m[i][j](第一行只计算了最后一个值):\n"<<endl;for(int i=1;i<=n;i++){cout<<"row "<<i<<": ";for(int j=0;j<=c;j++){if(i==1&&j<c)cout<<"   ";else    cout<<m[i][j]<<"  ";}cout<<endl;}}







下面来分析一下该动态规划算法的时间复杂性和空间复杂性:

显而易见上述算法计算Knapsack需要O(nc)计算时间,而Traceback需要O(n)计算时间。

但Knapsack算法有两个明显的缺点,1.要求算法所给的物品重量是wi(1<=i<=n)的整数,2.当背包容量很大时,算法需要计算的时间较多,例如,当c>2n次方是,算法Knapsack需要O(n2n)计算时间。




还有一种从上向下的动态规划算法:
#include <iostream>using namespace std;const int MIN=0x80000000;const int N=3;   //物品数量const int V=8;  //背包容量int f[N+1][V+1];int Package(int *W,int *C,int N,int V);void TraceBack(int N,int V,int *C,int *W);void main(int argc,char *argv[]){ int W[4]={0,7,5,8};      //物品权重 int C[4]={0,3,2,4};      //物品大小 Package(W,C,N,V); TraceBack(N,V,C,W); return;}void TraceBack(int N,int V,int *C,int *W){if(f[N][V]>0){cout<<"最优结果为:"<<f[N][V]<<endl;int i=N,j=V;while(i){ if(f[i][j]==(f[i-1][j-C[i]]+W[i])) {cout<<i<<":"<<"w="<<W[i]<<",c="<<C[i]<<endl;j-=C[i];}i--;} }elsecout<<"无最优值"<<endl;}int Package(int *W,int *C,int N,int V){ int i,j; memset(f,0,sizeof(f));  //初始化为0 //for(i=0;i<=N;i++) //for(j=1;j<=V;j++)               //此步骤是解决是否恰好满足背包容量,    //f[i][j]=MIN;                //若“恰好”满足背包容量,即正好装满背包,则加上此步骤,若不需要“恰好”,则初始化为0     for(i=1;i<=N;i++)  for(j=C[i];j<=V;j++)  {f[i][j]=(f[i-1][j]>f[i-1][j-C[i]]+W[i])?f[i-1][j]:(f[i-1][j-C[i]]+W[i]);  } return f[N][V];}

此种算法详情请见博客:http://www.cnblogs.com/fly1988happy/archive/2011/12/13/2285377.html







0 0