0-1背包问题
【问题】
有一个贼在偷窃一家商店时发现有N件物品;第i件物品值pi元,重wi磅(1≤i≤N),且都是整数。
他希望带走的东西越值钱越好,但他的背包中最多能装下M磅的东西(整数)。
如果每件物品或被带走或被留下,小偷应该带走哪几样东西?
【算法解析】
令f(i,y) 表示容量为y,物品i,i+1,···,n 的优化效益值,按优化原理可列递归关系如下:
初始背包问题的递归方程
f(1,c)=max{f(2,c), f(2,c-w1)+p1}
迭代
计算从f(n, *)开始((1)式)
然后应用(2)式递归计算f(i,*) ( i=n-1,n-2,···,2 ),
最后得出 f(1,c)
【代码】
(1)递归法:
#include <iostream> using namespace std; const int n=3; int w[n]={100,14,10}; int p[n]={20,18,15}; int F(int i, int y) { if (i == n) return (y < w[n]) ? 0 : p[n]; if (y < w[i]) return F(i+1,y); return max(F(i+1,y), F(i+1,y-w[i]) + p[i]); } int main() { cout<<F(0,116); return 0; }
时间复杂度分析:
程序的最坏时间复杂性t(n)
t(1)=a;
t(n)=2t(n-1)+b (n>1),其中a,b为常数
求解可得t(n)=Θ(2^n)
(2)迭代法:
#include <iostream> using namespace std; //该算法用二维数组f [i][y]来保存各个 f 的值 //二维数组需Θ((n+1)*(c+1))空间 template<class T> void Knapsack(T p[], int w[], T** f, int c, int n) { //f(n,y) int yMax = min(w[n]-1,c); for (int y = 0; y <= yMax; y++) f[n][y] = 0; for (int y = w[n]; y <= c; y++) f[n][y] = p[n]; // compute remaining f's for (int i = n - 1; i > 1; i--){ yMax = min(w[i]-1,c); for (int y = 0; y <= yMax; y++) f[i][y] = f[i+1][y]; for (int y = w[i]; y <= c; y++) f[i][y] = max(f[i+1][y],f[i+1][y-w[i]] + p[i]); } //i=1时单独处理! f[1][c] = f[2][c]; if (c >= w[1]) f[1][c] = max(f[1][c], f[2][c-w[1]] + p[1]); } //函数Traceback从f[i][y]产生优化的xi值 //Traceback的复杂性为Θ(n). template<class T> void Traceback(T **f, int w[], int x[], int c, int n) { // Compute x for optimal filling. for (int i = 1; i < n; i++){ if (f[i][c] == f[i+1][c]) x[i] = 0; else { x[i] = 1; c -= w[i]; } } x[n] = (f[n][c]) ? 1 : 0; } int main() { const int c=116; const int n=3; int w[n+1]={-1,100,14,10}; int p[n+1]={-1,20,18,15}; int **f=new int*[n+1]; for(int i=0;i<=n;i++){ f[i]=new int[c+1]; } int x[n+1]; Knapsack(p,w,f,c,n); Traceback(f,w,x,c,n); cout<<f[1][c]<<endl; for(int i=1;i<=n;i++){ cout<<x[i]<<"\t"; } delete[]f; return 0; }
上述程序有两个缺点:
1)要求物品重量为整数;
2)当背包容量c 很大时,例如c>2n,程序的复杂性为Ω(n2n).