背包九讲:P01: 01背包问题

实例原帖:Solving 0/1 knapsack problem

《背包九讲》没有实例无法真正理解,所以找了实例与大家分享。


问题

你的背包承重为 Capacity, 现在有n个物品,物品重量 w[n], 物品价值 v[n]。如何选择物品价值最大?

特点:每种物品仅有一件,可以选择放或不放, 即01.

实例输入:背包承重量10,5件物品,重量[2,3,3,4,6], 价值[1,2,5,9,4].

动态规划表格

table.png

表格说明

  • 第一行: 表示最大允许重量 0 ~ 10。

    • 比如7那一列 表示我们将背包的最大承重量当作7来求解。
  • 第一列: 表示物品的 重量(价值)。

    • 比如第四行 3(5): 我们只考虑物品集合{ 2(1),3(2),3(5)},剩下的 4(9),6(4) 当做不存在。
  • 表格中具体某一个数,表示当前行列条件下能获取的最大价值。

    • 比如第五行=4(9)第八列=6, 意味着当背包最大承重量为6,从 2(1),3(2),3(5),4(9)中挑选物品可以获取的最大价值,即10.

如何生成表格

比如最大承重量为5,第三行只考虑2(1),3(2)的情况:

  • 如果不拿3(2), 那么获取的价值可以从上一行得到,即只有2(1)的情况,结果为1.
  • 如果拿3(2),那么获取的价值为 2 + 最大承重量为(5-3)=2的时候拿除了3(2)的物品能获得的最大价值,这个数值能从上一行最大承重2的那列获取,结果为3.

综上,应该拿3(2),最大价值为3.

总结

第i行,j列的数值应该等于: max (dp[i-1][j], v[i]+ dp[i-1][j-w[i]])

代码


#include <iostream>
#include <vector>

using namespace std;

// Input
// 5(n) 10(capacity)
// 1 2 5 9 4 (v[n])
// 2 3 3 4 6 (w[n])

int maxValue (int n , int c , vector<int> v, vector<int> w){
    int **dp = new int*[n];

    for(int i = 0; i < n; i++){
        dp[i] = new int[c+1];
        for(int j = 0 ; j <= c; j++)
            dp[i][j] = 0;
    }

    // Populate the first row
    for(int j = 0 ; j <= c ; j++)
        dp[0][j] += j >= w[0] ? v[0] : 0;

    // Populate the rest
    for(int i = 1 ; i < n; i++ ){
        for(int j = 0; j <= c; j++){
            if(j<w[i]) dp[i][j] = dp[i-1][j];
            else dp[i][j] = max(v[i]+dp[i-1][j-w[i]], dp[i-1][j]);
        }
    }

    return dp[n-1][c];
}

int main() {
    int n = 0, capa = 0;
    cin >> n >> capa;

    vector<int> values(n,0);
    vector<int> weights(n,0);

    for(int i = 0; i < n ; i++)
        cin >> values[i];

    for(int j = 0; j < n; j++)
        cin >> weights[j];

    cout << maxValue (n,capa,values,weights) << endl;

    return 0;
}

KirkBai
27 声望6 粉丝