背包问题总结

来源:互联网 发布:博美堂脂肪燃烧弹淘宝 编辑:程序博客网 时间:2024/04/28 20:40

一、背包问题描述

背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:有N件物品和一个容量为V的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。它是在1978年由Merkel和Hellman提出

背包问题包括0-1背包问题、完全背包问题、部分背包问题等多种变种。其中,最简单的是部分背包问题,它可以采用贪心法来解决,而其他几种背包问题往往需要动态规划来求解。

二、部分背包问题

已知:有N件物品和一个容量为V的背包。第i件物品的重量是weight[i],价值是value[i]

限制:不要求把物品整个装入,可以只装入一个物品的部分

问题:解将哪些物品装入背包可使价值总和最大

1、基本原理

部分背包问题常采用贪心算法来解决,先对每件物品计算其每单位重量价值value[i]/weight[i],然后从具有最大单位价值的物品开始拿,然后拿第二大价值的物品,直到装满背包。按照这种贪心策略拿到的必然是价值总和最大。
2、代码

#include<iostrem>#include<algorithm>using namespace std;const int N=5;//物品数量const int V=8;//背包最大容量const int value[]={0,3,4,5,6,7};//物品的价值const int weight[]={0,2,3,4,5,6};//物品的重量double a[N+1]={0};void Ratio(){for(int i=1;i<=N;++i){a[i]=(double)value[i]/weight[i];}}double Knapsack(double sum){double f=0;while(sum>0){double max=0;int k;for(int i=0;i<N+1;i++)//找到当前可获得效益最大的物体,并记住其下标{if(a[i]>max){max=a[i];k=i;//记住下标}}if(weight[k]<sum){f=f+value[k];cout<<f<<endl;sum=sum-weight[k];a[k]=0;//当前物体加入背包中后将其效益置零,防止重复计算}else{f=f+sum*a[k];sum=0;}}return f;}int main(){double sum=V;Ratio();cout<<Knapsack(sum)<<endl;system("pause");return 1;}

三、01背包问题

已知:有一个容量为V的背包和N件物品,第i件物品的重量是weight[i],收益是value[i]。

限制:每种物品只有一件,可以选择放或者不放

问题1:在不超过背包容量的情况下,能获得的最大价值或收益

问题2:在恰好装满背包的情况下,能获得的最大价值或收益

1、基本原理

(1)01背包的特点:每种物品只有一件,可以选择放或者不放。

(2)用子问题定义状态f[i][v]表示前i件物品放到一个容量为v的背包中可获得的最大价值。

(3)状态转移方程f[i][v]=max{f[i-1][v],f[i-1][v-weight[i]]+value[i]}

考虑“将前i件物品放入容量为v的背包中这个子问题”,若只考虑第i件物品,它有两种选择,放或者不放

1) 如果第i件物品不放入背包中,那么问题就转换为:将前i-1件物品放到容量为v的背包中,带来的收益f[i-1][v]

2) 如果第i件物品能放入背包中,那么问题就转换为:将前i-1件物品放到容量为v-weight[i]的背包中,带来的收益f[i- 1][v-weight[i]]+value[i]

2、代码

#include<iostream>using namespace std;const int N=5;//物品数量const int V=10;//背包最大容量const int value[]={0,3,4,5,6,7};//物品的价值const int weight[]={0,2,3,4,5,6};//物品的重量int f[N+1][V+1]={{0}};int max(int x,int y){return x>y ? x:y;}int Knapsack(){memset(f,0,sizeof(f));for(int i=1;i<=N;++i){for(int j=0;j<=V;++j){if(j>=weight[i])f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i]);//状态转移方程}}return f[N][V];}int main(){cout<<Knapsack()<<endl;system("pause");return 1;}

3、效率分析

此算法的时间和空间复杂度均为O(N*V)。其中,N表示物品个数,V表示背包容量。时间复杂度不可以再优化了,但是空间复杂度可以优化到O(V)

4、优化空间复杂度

(1)方法:采用一维数组f[v]取代二维数组 f[i][v] 保存中间状态。这样,第i次循环后,f[v]中存储的是前i个物体放到容量v时的最大价值。

(2)原理:由状态转移方程f[i][v]=max{f[i-1][v],f[i-1][v-weight[i]]+value[i]}可知要想得到 f[i][v],我们需要知道f[i - 1][v] 和 f[i - 1][v - weight[i]],当使用二维数组保存中间状态时,这两个状态可以直接取出。但当使用一维数组存储状态时,f[v]表示,在执行i次循环后(此时已经处理i个物品),前i个物体放到容量v时的最大价值,即之前的f[i][v]。与二维相比较,它把第一维隐去了,但是二者表达的含义还是相同的,只不过针对不同的i,f[v]一直在重复使用,所以,也会出现第i次循环可能会覆盖第i - 1次循环的结果。

为了求f[v],我们需要知道,前i-1个物品放到容量v的背包中带来的收益,即之前的f[i-1][v]和前i-1件物品放到容量为v-weight[i]的背包中带来的收益,即之前的f[i-1][v-weight[i]]。由于使用一维数组存储,则在求这两个子问题时就无法直接取出,因为第i次循环可能会覆盖第i-1次循环的结果。现在我们来求这两个值

1)前i-1个物品放到容量v的背包中带来的收益,即之前的f[i-1][v]

由于,在执行在i次循环时f[v]存储的是前i个物体放到容量v时的最大价值,在求前i个物体放到容量v时的最大价值(即之前的f[i][v])时,我们是正在执行第 i 次循环,f[v]的值还是在第i-1次循环时存下的值,在此时取出的f[v]就是前i-1个物体放到容量v时的最大价值,即f[i-1][v]。

2)前i-1件物品放到容量为v-weight[i]的背包中带来的收益,即之前的f[i-1][v-weight[i]]

由于,在执行第i次循环前,f[0~V]中保存的是第i-1次循环的结果,即是前(i-1)个物体分别放到容量0~V时的最大价值,即f[i-1][0~V]。则在执行第i次循环前,f数组中v-weight[i]的位置存储就是我们要找的前i-1件物品放到容量为v-weight[i]的背包中带来的收益(即之前的f[i-1][v-weight[i]]),这里假设物品是从数组下标1开始存储的。

(3)伪代码

<span style="font-size:14px;">for i=1..N //枚举物品      for v=V..0 //枚举容量,从大到小          f[v]=max{f[v],f[v-weight[i]]+value[i]};</span>

由上面伪代码可知,在执行第i次循环时,需要把背包容量由V..0都要遍历一遍,检测第i件物品是否能放。

(4)逆序枚举容量的原因

注意,我们是由第i-1次循环的两个状态推出第i个状态的,而且v>v-weight[i],则对于第i次循环,背包容量只有当V..0循环时,才会先处理背包容量为v的状况,后处理背包容量为v-weight[i]的情况。

具体来说,由于在执行v时,还没执行到v-weight[i]的,因此,f[v-weight[i]]保存的还是第i-1次循环的结果。即在执行第i次循环且背包容量为v时,此时的f[v]存储的是f[i-1][v],此时f[v-weight[i]]存储的是f[i-1][v-weight[i]]。

相反,如果在执行第i次循环时,背包容量按照0..V的顺序遍历一遍,来检测第i件物品是否能放。此时在执行第i次循环且背包容量为v时,f[v]存储的是f[i-1][v],但是,而f[v-weight[i]]存储的是f[i][v-weight[i]]。因为,v>v- weight[i],第i次循环中,执行背包容量为v时,容量为v-weight[i]的背包已经计算过,即f[v-weight[i]]中存储的是f[i][v-weight[i]]。综上可知,对于01背包,按照增序枚举背包容量不可行。

(5)完整代

#include<iostream>using namespace std;const int N=5;//物品数量const int V=8;//背包最大容量const int value[]={0,3,4,5,6,7};//物品的价值const int weight[]={0,2,3,4,5,6};//物品的重量int f[V+1]={{0}};int max(int x,int y){return x>y ? x:y;}int  Knapsack(){memset(f,0,sizeof(f));for(int i=1;i<=N;++i){for(int j=0;j<=V;++j){//f[j]=f[i-1][j];if(j>=weight[i])f[j]=max(f[j],f[j-weight[i]]+value[i]);//状态转移方程}}return f[V];}int main(){cout<<Knapsack()<<endl;system("pause");return 1;}

5、增序枚举背包容量与逆序枚举背包容量的差异

(1)产生的效果:增序枚举背包容量重复的装入某个物品,而且尽可能多的,使价值最大,当然不会超过背包容量。而逆序枚举背包容量,背包中的物品至多装一次使价值最大,当然不会超过背包容量。

(2)举例说明

    逆序枚举物品

http://img.my.csdn.net/uploads/201302/15/1360890018_8745.jpg

当i=2,我们要求f[5]:表示检测物品2放入容量为5的背包的最大收益。上图表示,当i=2,求f[5]时f数组的状况,橙色为数组现在存储的值,这些值是i=1时(上一次循环)存入数组f的。相当于f[i-1][v]而黄色使我们要求的值,在求f[5]之前,f[5]= 5,即f[i-1][5]=5。现在要求i=2时的f[5]=f[5-2]+10=5+10=15>f[i-1][5]=5。故,f[5]=15;注意,在求f[v]时,它引用的f[v-weight[i]]和f[v]都是上一次循环的结果。

    顺序枚举物品

当i=2,我们要求f[5]:表示检测物品2放入容量为5的背包的最大收益。上图表示,当i=2,求f[5]时f数组的状况,橙色为数组现在存储的值,这些值是i=2时(本次循环)存入数组f的。相当于f[i][v]。这是由于,我们是增序遍历数组f的,在求f[v时,v之前的值(0~v-1)都已经在第i次循环中求出。而黄色使我们要求的值,在求f[5]之前,f[5]=5,即f[i-1][5]=5。现在要求 i=2时的f[5]=f[5-2]+10=10+10=20>f[i-1][5]=5。故,f[5]=20;其中引用的f[3]是相当于f[i][3] 而不是正常的f[i-1][3]。注意,在求f[v]时,它引用的f[v-weight[i]]是本次循环的结果而f[v]是上一次循环的结果。

换个角度说,在检测背包容量为5时,看物品2是否加入。由状态转移方程可知,我们f[5]需要引用自己本身和f[3]。由于背包容量为3时,可以装入物品2,且收益比之前的大,所以放入背包了。在检测f[5]时,肯定要加上物品2的收益,而f[5]在引用f[3]时,f[3]时已经加过一次物品2,因此,在枚举背包容量时,物品2会加入多次。

我们观察二维状态转移方程:f[i][v]=max(f[i-1][v],f[i-1][v-weight[i]]+value[i])。首先我们明确三个问题:

1) v-weight[i]<v。

2) 状态f[i][v]是由f[i-1][v]和f[i-1][v-weight[i]] 两个状态决定。

3) 对于物品i,我们在枚举背包容量时,只要背包容量能装下物品i且收益比原来的大,就会成功放入物品i。

若以递增的顺序枚举背包容量的话的话,由于v-weight[i]<v,则会先计算v-weight[i]。在背包容量为v-weight[i]时,一旦装入了物品i,由于求f[v]需要使用f[i-1][v-weight[i]],而若求f[v]时也可以装入物品i的话,那么在背包容量为v时,容量为v的背包就装入可两次物品。又若v-weight[i]是由之前的状态推出,它们也成功装入物品i的话,那么容量为v的背包就装入了多次物品i了。注意,此时,在计算f[v]时,已经把物品i能装入的全装入容量为v的背包了,此时装入物品i的次数为最大。其实,顺序枚举容量是完全背包问题最简捷的解决方案。

6、不超过和恰好满足背包容量的算法对比

1、初始化的细节问题

求最优解的背包问题时,有两种问法:

1)在不超过背包容量的情况下,最多能获得多少价值

2)在恰好装满背包的情况下,最多能获得多少价值

这两种问法的实现方法是在初始化的时候有所不同。

(1)恰好装满背包

使用二维数组f[i][v]存储中间状态,其中第一维表示物品,第二维表示背包容量初始化时,除了f[i][0]=0(第一列)外,其它全为-∞

1)原因:初始化f数组就是表示,在没有任何物品可以放入背包时的合法状态。对于恰好装满背包,只有背包容量为0(第一列),可以什么物品都不装就能装满,这种情况是合法情况,此时价值为0。其他f[0][v](第一列)是都不能装满的,此时有容量没物品。而其他位置(除去第一行和第一列的位置),我们为了在计算中比较最大值,也要初始化为负无穷。我们从程序的角度上看,我们只允许装入背包物品的序列的起始位置是从第一列开始,这些起始位置都是合法位置,且能恰好装满的情况收益均为正值,到f[N][V]终止。

注意,我们虽然是求恰好装满,还是需要枚举所有可以装入背包的物品,只要能装入,还需装入,收益有增加。只不过,由于恰好装满的物品的序列肯定是从第一列某行开始的,且之后的收益肯定是正值。对于非恰好装满的物品序列,其实位置肯定是从第一行某位置开始的,由于此时被初始化为负无穷,在和那些恰好装满物品序列带来的价值时,肯定是小的。所以,我们最后能获得最大值。

2)代码

#include<iostream>using namespace std;const int Min=0x80000000;//表示负无穷const int N=5;//物品数量const int V=8;//背包最大容量const int value[]={0,3,4,5,6,7};//物品的价值const int weight[]={0,2,3,4,5,6};//物品的重量int f[N+1][V+1]={{0}};int max(int x,int y){return x>y ? x:y;}int Knapsack(){int i,j;for(i=0;i<=N;++i)//初始化for(j=0;j<=V;++j){f[i][j]=Min;}    for(i=0;i<=N;++i)//背包容量为0是是合法状态f[i][0]=0;//memset(f,0,sizeof(f));for(i=1;i<=N;++i){for(j=1;j<=V;++j){f[i][j]=f[i-1][j];if(j>=weight[i])f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i]);//状态转移方程}}return f[N][V];}int main(){cout<<Knapsack()<<endl;system("pause");return 1;}

使用一维数组f[v]存储中间状态,那么在初始化时除了f[0]为0,其它f[1..V]均设为-∞,这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f[0..V]全部设为0。

1)原因:只有容量为0的背包可以什么物品都不装就能装满,此时价值为0。其它容量的背包均没有合法的解,属于未定义的状态,应该被赋值为-∞

2)代码

#include<iostream>using namespace std;const int Min=0x80000000;//表示负无穷const int N=5;//物品数量const int V=8;//背包最大容量const int value[]={0,3,4,5,6,7};//物品的价值const int weight[]={0,2,3,4,5,6};//物品的重量int f[V+1]={{0}};int max(int x,int y){return x>y ? x:y;}int Knapsack(){int i,j;f[0]=0;//初始化,只有背包容量为零时才是合法状态for(i=1;i<=N;++i)    f[i]=Min;for(i=1;i<=N;++i){for(j=V;j>=weight[i];--j){if(j>=weight[i])f[j]=max(f[j],f[j-weight[i]]+value[i]);//状态转移方程}}return f[V];}int main(){cout<<Knapsack()<<endl;system("pause");return 1;}

(2)不需要把背包装满

使用二维数组f[i][v]存储中间状态,其中第一维表示物品,第二维表示背包容量初始化时,f[0...N][0...V] = 0。使用一维数组f[v]存储中间状态,表示背包容量则f[0...v]=0。

1)原因

如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。

2)代码

前面已给出。

7、输出方案

为了清楚地知道有哪些物体加入背包中为了将其输出,可以参照一般动态规划问题输出方案的方法:记录下每个状态的最优值是由状态转移方程的哪一项推出来的,换句话说,记录下它是由哪一个策略推出来的。便可根据这条策略找到上一个状态,从上一个状态接着向前推即可。

由01背包的二维状态转移方程f[i][v] = max(f[i - 1][v],f[i - 1][v - weight[i]] + value[i])  ,对于状态f[i][v],它来自两种策略,可以是f[i - 1][v],也可以是f[i - 1][v - weight[i]] + value[i]。对于第二种情况,就是把物品i放入了背包。根据状态转移方程,我们可以给出两种实现方法

1) 借助存储状态的数组,直接根据状态转移方程倒着推

检测是否满足f[i][v] == f[i - 1][v - weight[i]] + value[i]如果满足,则把第i件物品放入了,此时我们要检测第i - 1件物品,背包容量为v - weight[i],不满足则表示没有把第i件物品放入,直接检测第i - 1件物品,此时背包容量还是v。注意,这种方法只适用于存储状态数组不压缩的情况。压缩数组由于数据有覆盖,不能使用。

代码

<pre name="code" class="cpp">#include<iostream>using namespace std;const int Min=0x80000000;//表示负无穷const int N=5;//物品数量const int V=8;//背包最大容量const int value[]={0,3,4,5,6,7};//物品的价值const int weight[]={0,2,3,4,5,6};//物品的重量int f[N+1][V+1]={{0}};int max(int x,int y){return x>y ? x:y;}int Knapsack(){int i,j;for(i=0;i<=N;++i)//初始化for(j=0;j<=V;++j){f[i][j]=Min;}    for(i=0;i<=N;++i)//背包容量为0是是合法状态f[i][0]=0;//memset(f,0,sizeof(f));for(i=1;i<=N;++i){for(j=1;j<=V;++j){f[i][j]=f[i-1][j];if(j>=weight[i])f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i]);//状态转移方程}}return f[N][V];}void PrintKnapsack()//逆序输出物品编号{int i=N,j=V;cout<<"加入背包的物体的逆序编号为:"<<endl;while(i){if(f[i][j]==f[i-1][j-weight[i]]+value[i])//满足则输出{cout<<i<<" ";    j=j-weight[i];}i--;}cout<<endl;}void PrintKnapsack_recursion(int i,int j)  //顺序输出物体编号,采用递归法{      if (i==0||j==0)      {          return;      }      if (f[i][j] == f[i-1][j-weight[i]]+value[i])      {       PrintKnapsack_recursion(i-1,j-weight[i]);cout<<i<<" ";              }elsePrintKnapsack_recursion(i-1,j);}  int main(){cout<<Knapsack()<<endl;PrintKnapsack();cout<<"加入背包物体的顺序编号为:"<<endl;PrintKnapsack_recursion(N,V);system("pause");return 1;}

2)另外开辟数组,在求解最大收益时做标记位

求解完最大收益后,根据这个新数组倒着推结果。基本思想是,对于现在这个状态的位置,它存储的是该状态上一位置。注意,这种方法均适用存储状态数组不压缩和压缩两种情况

代码:

<span style="font-size:12px;">#include<iostream>using namespace std;const int Min=0x80000000;//表示负无穷const int N=5;//物品数量const int V=8;//背包最大容量const int value[]={0,3,4,5,6,7};//物品的价值const int weight[]={0,2,3,4,5,6};//物品的重量int f[N+1][V+1]={{0}};int g[N+1][V+1]={{0}};//求背包序列int max(int x,int y){return x>y ? x:y;}int Knapsack(){int i,j;for(i=0;i<=N;++i)//初始化for(j=0;j<=V;++j){f[i][j]=Min;}    for(i=0;i<=N;++i)//背包容量为0是是合法状态f[i][0]=0;//memset(f,0,sizeof(f));for(i=1;i<=N;++i){for(j=1;j<=V;++j){f[i][j]=f[i-1][j];if(j>=weight[i]){f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i]);//状态转移方程g[i][j]=1;}}}return f[N][V];}void PrintKnapsack()//逆序输出物品编号{int i=N,j=V;cout<<"加入背包的物体的逆序编号为:"<<endl;while(i){if(g[i][j]==1)//满足则输出{cout<<i<<" ";    j=j-weight[i];}i--;}cout<<endl;}void PrintKnapsack_recursion(int i,int j)  //顺序输出物体编号,采用递归法{      if (i==0||j==0)      {          return;      }      if (g[i][j])      {       PrintKnapsack_recursion(i-1,j-weight[i]);cout<<i<<" ";              }elsePrintKnapsack_recursion(i-1,j);}  int main(){cout<<Knapsack()<<endl;PrintKnapsack();cout<<"加入背包物体的顺序编号为:"<<endl;PrintKnapsack_recursion(N,V);system("pause");return 1;}</span>

采用一维数组的输出形式

代码

<span style="font-size:12px;">#include<iostream>using namespace std;const int Min=0x80000000;//表示负无穷const int N=5;//物品数量const int V=8;//背包最大容量const int value[]={0,3,4,5,6,7};//物品的价值const int weight[]={0,2,3,4,5,6};//物品的重量int f[V+1]={{0}};int g[N]={0};int max(int x,int y){return x>y ? x:y;}int Knapsack(){int i,j;f[0]=0;//初始化,只有背包容量为零时才是合法状态for(i=1;i<=N;++i)    f[i]=Min;for(i=1;i<=N;++i){for(j=V;j>=weight[i];--j){if(j>=weight[i]){f[j]=max(f[j],f[j-weight[i]]+value[i]);//状态转移方程if(f[j]==f[j-weight[i]]+value[i])g[i]=1;}}}return f[V];}void PrintKnapsack()//逆序输出加入背包的物体的编号{int i=N,j=V;cout<<"加入背包的物体的逆序编号为:"<<endl;while(i){if(g[i]==1){cout<<i<<" ";j=j-weight[i];}i--;}cout<<endl;}int main(){cout<<Knapsack()<<endl;PrintKnapsack();system("pause");return 1;}</span>


四、完全背包问题

已知:有N种物品和一个容量为V的背包,第i种物品的重量是w[i],价值是v[i]。

限制:每种物品都有无限件可用。

问题:求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

1、基本原理
完全背包问题与01背包问题类似,唯一不同的就是每种物体有无限件可用。也就是若从每件物品出发,与其相关的策略并非是取或是不取两种,而是取0件、取1件、取2件......等。若仍按照解01背包的思路来解此题,令f[i][v]表示前i种物品恰放入一个容量为v的背包获得的最大利益。

转态转移方程:f[i][v]=max{f[i-1][v-k*w[i]]+k*value[i]|0<=k*w[i]<=v}

伪代码:

<span style="font-size:12px;">for i=1....N     for j=0.....V          for k=1....j div w[i]                f[i][j]=max{f[i-1][j],f[i-1][j-k*w[i]]+k*value[i]};</span>

2、代码

<span style="font-size:12px;">#include<iostream>using namespace std;const int Min=0x80000000;//表示负无穷const int N=5;//物品数量const int V=8;//背包最大容量const int value[]={0,3,4,5,6,7};//物品的价值const int weight[]={0,2,3,4,5,6};//物品的重量int f[N+1][V+1]={{0}};int max(int x,int y){return x>y ? x:y;}int Knapsack(){int i,j;for(i=0;i<=N;++i)//初始化for(j=0;j<=V;++j){f[i][j]=Min;}    for(i=0;i<=N;++i)//背包容量为0是是合法状态f[i][0]=0;//memset(f,0,sizeof(f));for(i=1;i<=N;++i){for(j=0;j<=V;++j){            for(int k=0;k<=j/weight[i];++k)    {     f[i][j]=f[i-1][j];     if(j>=k*weight[i])     f[i][j]=max(f[i-1][j],f[i-1][j-k*weight[i]]+k*value[i]);//状态转移方程     }}}return f[N][V];}int main(){cout<<Knapsack()<<endl;system("pause");return 1;}</span>

该问题跟01背包问题一样有O(N*V)个状态需要求解,但求解每个状态的时间则不是常数了,求解状态f[v]的时间是O(v/c),总的复杂度是超过O(VN)的。

3、空间优化

01背包中,可以使用一维数组来优化空间,完全背包也可以。

伪代码

<span style="font-size:12px;">for i=1....,N    for j=0....V        f[j]=max{f[j],f[j-weight[i]]+value[i]};</span>

代码

<span style="font-size:12px;">#include<iostream>using namespace std;const int Min=0x80000000;//表示负无穷const int N=5;//物品数量const int V=8;//背包最大容量const int value[]={0,3,4,5,6,7};//物品的价值const int weight[]={0,2,3,4,5,6};//物品的重量int f[V+1]={{0}};int max(int x,int y){return x>y ? x:y;}int Knapsack(){int i,j;f[0]=0;//初始化,只有背包容量为零时才是合法状态for(i=1;i<=N;++i)    f[i]=Min;for(i=1;i<=N;++i){for(j=0;j<=V;++j){if(j>=weight[i]){f[j]=max(f[j],f[j-weight[i]]+value[i]);//状态转移方程}}}return f[V];}int main(){cout<<Knapsack()<<endl;system("pause");return 1;}</span>
至于为何j=0.....V顺序来循环,01背包中已经给出了详细的解释。
同时,该算法也可以以另外的思路得出。基本思路中的妆态转移方程可以等价地变形成如下形式:

<span style="font-size:12px;">f[i][j]=max{f[i-1][j],f[i][v-weight[i]]+value[i]}</span>


参考:

1、http://blog.csdn.net/insistgogo/article/details/8579597

2、http://blog.csdn.net/sgbfblog/article/details/7908837

3、http://baike.baidu.com/link?url=d-HW4oj6tdTDmyOZ6bCAAVwTCRoM2AUCM6bxt0FisDnzLl3KlkyXa6YCmZwAt1Z-BDhfIPRy22HTAkU_TgBR9_

0 0