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]<<"	";
    }
    
    delete[]f;
    return 0;
}

上述程序有两个缺点:
1)要求物品重量为整数;
2)当背包容量c 很大时,例如c>2n,程序的复杂性为Ω(n2n).

原文地址:https://www.cnblogs.com/wxgblog/p/0-1beibaowenti.html