您的位置:首页 > 其它

0-1背包

2015-11-12 21:41 162 查看
有n种物品,第i种物品的体积为V[i],重量为W[i],选一些物品放到容量为c的背包里,使得背包内的物品在总体积不超过C的前提下重量尽量大。

用d(i,j)表示当前处于第i层,背包的剩余重量为j,通俗点说就是把 i i+1 i+2 .....n 个物品装到容量为j的背包中的最大总重量。

所以我们得到的状态转移方程为:

d(i+1,j) ( 0=<j<v[i])

d(i,j)= max( d(i+1,j) , d(i+1,j-v[i])+w[i] ) (j>v[i])

最终的答案为 d(1,c)

#include<iostream>
#define MAX 100
using namespace std;
int main(){
int n,c;
int d[MAX][MAX];
int v[MAX],w[MAX];
while(cin>>n>>c){
memset(d,0,sizeof(d));
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
for(int i=n;i>=1;i--){
for(int j=0;j<=c;j++){
d[i][j]=(i==n ? 0: d[i+1][j])  ;
if(j>=v[i])
d[i][j]=max(d[i][j],d[i+1][j-v[i]]+w[i]);
}
}
cout<<d[1][c]<<endl;
}
return 0;
}


聪明的读者也许看出来了,还有另外一种对称的状态的定义:

用f(i,j) 表示把前i个物品装到容量为j的背包中的最大重量,其转移方程也不难得出

f(i,j)=max(f(i-1,j),f(i-1,j-v[i])+w[i])

最终的答案为 f(n,c);

于是上面的代码就可以改写为

for(int i=1;i<=n;i++){
for(int j=0;j<=c;j++){
f[i][j]=(i==1?0:f[i-1][j]);
(if j>=v[i] )f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
}
}


  

看上去两种方式是完全对称的,但是还是有细微差别的,新的允许边读边写,而不必把w,v保存下来。

for(int i=1;i<=n;i++){
for(int j=0;j<=c;j++){
cin>>v>>w;
f[i][j]=(i==1?0:f[i-1][j]);
f[i][j]=max(f[i][j],f[i-1][j-v]+w);
}
}


更为奇妙的是还可以把数组f变成一维:

for(int i=1;i<=n;i++){
cin>>v>>w;
for(int j=c;j<=0;j--){
if(j>=v){
f[j]=max(f[j],f[j-v]+w);
}
}
}


值得注意的是j一定要逆序。

如果是顺序的话,在一次i的循环中就会利用前面的结果,从而导致不是在这一次中放一个物品,而是多个的假象,读者可以自行试试。

在递推法中,如果计算顺序很特殊,而且计算新状态所用到的原状态不多的话,可以尝试用滚动数组来减少内存开销。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: