背包九讲

0-1背包问题

有n个重量和价值分别为 wi,vi的物品。从这些物品中挑选出总重量不超过 WW的物品, 求所有挑选方案中价值总和的最大值。

样例:

n=4 (w,v)=(2,3),(1,2),(3,4),(2,2) W=5

n个物品,每种物品只有两种选择,放进背包,或者不放进背包。n个物品对应的,最大的所有可能的总数为 2n2n 种不同的放法。 
最朴素的,我们可以枚举所有可能的放法,找到最大值。

Rec(n,W) 表示剩余容量为W的背包,还有1~n 这nn个物品可以选择, 所能取得的最大的背包价值。
还剩i个物品可以选, 背包容量剩余j的时候,可以分为两种情况:
1. 第i个物品不选,只考虑前i−1个物品。则 Rec(i,j)=Rec(i−1,j),此时背包容量j不变
2. 第i个物品被选择, 接下来要考虑的就是, 如何在前i−1个物品中选择物品放入容量为j−w[i]的背包, 则 Rec(i,j)=Rec(i−1,j−w[i])+v[i]

总和两种可能,取较大值。可以给出递推式: 
Rec(i,j)=max(Rec(i−1,j),Rec(i−1,j−w[i])+v[i])

还需要考虑一些细节: 
1. 终止条件, i=0 ,无物品可选 Rec(0,j)=0。
0个物品可以选择,放入容量为j的背包,得到的最大价值只能为0 
2. j<w[i],背包剩余容量不足以放下第i个物品,Rec(i,j)=Rec(i−1,j)R
综上,得到递推式: 
背包九讲

递归方法:

#include <iostream>
using namespace std;
int a[5]={3,4,2,7,8};
int b[5]={9,2,4,5,3};
int w=5,n=5;
int res(int i,int j){
     int rec;
    if(i==0){//终止条件,无物品可选Rec(0,j)=0
        //0个物品可以选择,放入容量为j的背包,得到的最大价值只能为0
        rec=0;
    }
    else if(j<a[i]){
        //背包剩余量不足以放下第i个物品
        rec=res(i-1,j);
    }
    else{
        //抉择,第i个物品选或者不选,都试一下,去最大值
        rec=__max(res(i-1,j),res(i-1,j-a[i])+b[i]);
    }
  return rec;
}
int main(){
    cout<<res(n,w)<<endl;
    return 0;
}

从上图可以看出,我们枚举了所有可能的情况,即对于每个物品i,物品i选还是不选,我们都考虑了一遍。递归的层数是n+1层,最后一层是判定终止。

再来重申一下 Rec(i,j) 的含义,表示有编号为1~ i 的这前 i 个物品可以选择,放入容量为j 的背包,所能达到的最大价值。终止条件是i=0,时,无物品可选,Rec(0,j)=0,题目要求的是 Rec(n,W)。
在求解Rec(n,W)的过程中,有些情况可能会被重复计算。比如上图Rec(1,2) 在第四行计算了2次。这种重复计算是随着n的增大,指数级增长的,所以n,Wn,W 较大时,问题就是不可解的。时间和空间复杂度太高 ,为O(2n) 。
动态规划求解:

背包九讲

代码示例

#include <iostream>
#include <cstring>
#define MAXN 1000
using namespace std;

int dp[MAXN][MAXN];
int w[MAXN] = {0, 2, 1, 3, 2};
int v[MAXN] = {0, 3, 2, 4, 2};
int W = 5, n = 4;

int solve(int n, int W) {
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= n; i++) { //i从1开始,因为i=0的值已经确定为0
        for (int j = 0; j <= W; j++) {
            if (j < w[i]) {
                dp[i][j] = dp[i-1][j];
            }
            else {
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
            }
        }
    }
    return dp[n][W];
}

int main() {
  cout << solve(n, W) << endl;
  return 0;
}

 

上一篇:平方拆分(dfs)


下一篇:《算法竞赛入门经典(第2版)》第三章习题题解