0-1背包问题
一个正在抢劫商店的小偷发现了n个商品,第i个商品价值pi,重wi磅。这个小偷希望拿走价值尽量高的商品,但他的背包最多能容纳W磅重的商品,W是一个整数。他应该拿走哪些商品??(这个问题是0-1背包问题,因为小偷要么拿走一件商品,要么留下,不能拿走商品一部分,或者把一个商品拿多次)。
0-1背包问题主要问题就是怎么找到最优子结构以及递推式子,我们不妨设f[i,v]为前i个商品,拿走容量为v磅重的商品的最大价值,由此可以得到递归式:
f(i,v) = max{ f(i-1,v), f(i-1,v-w[i])+p[i] }
由上述递归式子我们进行分析,在已知前i-1个商品产生最大价值的基础之上,我们要求前i个商品,拿走容量为v磅商品的最大价值。一样我们就可以分为两种情况,第一种是我们拿了第i件商品,这种情况下最大价值就是第i件商品的价格加上前i-1件商品,拿走容量为v-w[i]磅商品的最大价值;第二种情况是我们没有拿第i件商品,这种情况下最大价值为前i-1件商品,拿走容量为v-w[i]磅商品的最大价值,比较上述两种情况下的最大价值,最大的为我们需要的结果。
理解了该递归式,我们的程序就好写了。
具体程序如下:
#include"stdio.h"
#include"malloc.h"
#define num 3 //物品数量
#define weight 50 //背包大小
int backpack(int W,int *w,int *p,int n); //找出最优解
int print(int **m,int *w,int n,int x); //打印出最优解方案
void main()
{
int W=50;
int w[num]={10,20,30}; //物品重量
int p[num]={60,100,120}; //物品价格
printf("%d",backpack(W,w,p,n));
getchar();
}
int backpack(int W,int *w,int *p,int n)
{
int f[num][weight+1]; //f[num][weight]代表前num个物品最大承重重量为weight的时候价格的价格
int **m;
int i,v;
m=(int **)malloc(num*sizeof(int)); //为二维指针申请空间
for(i=0;i
(f[i-1][v-w[i]]+p[i])) //比较选择
{
f[i][v]=f[i-1][v];
m[i][v]=i-1; //记录当前选择的物品
}
else
{
f[i][v]=f[i-1][v-w[i]]+p[i];
m[i][v]=i;
}
}
}
}
print(m,w,2,weight);
return f[n-1][weight];
}
int print(int **m,int *w,int n,int x)
{
int i=n;
if(n<0)
{
return 0; //终止条件
}
if(m[i][x]==i) //如果拿当前物品,则直接输出
{
printf("%d ",m[i][weight]);
print(m,w,i-1,x-w[i]);
}
else //如果没拿当前物品,则递归下一个
{
print(m,w,i-1,x);
}
return 1;
}
在递推式的基础之上,我们还要考虑初始情况,当i=1时(即只有一件物品),当只有一件物品时,容量小于该件物品时,价值为0;当容量大于该件物品时,价值为该物品的价值。在这基础上,我们还要考虑到求第i件商品的最大价值是,当容量小于第i件物品的重量时,我们应该选择第i-1件商品的最大价值作为第i件商品的最大价值。
由上可知,程序的时间复杂度为O(nW),空间复杂度为O(nW),但是由递归式可知,我们只需要f(i-1,v)和f(i-1,v-w)的数据就可以得到下一时刻的值,这样就可以减少我们程序的空间复杂度到O(2W),这种方法可以考虑一下。