0-1背包问题


目录
  • 描述
  • 方法:动态规划

题目来源:01背包 | 牛客网

描述

已知一个背包最多能容纳体积之和为v的物品。现有 n 个物品,第 i 个物品的体积为 vi , 重量为 wi。求当前背包最多能装多大重量的物品?

数据范围: 1≤v≤1000,1≤n≤1000,1≤n≤1000 , 1≤vi≤1000, 1≤wi≤1000

进阶:O(n?v)

方法:动态规划

每个物品只有2种状态:放入(0),不放入(1),也因此称为01背包问题。

假设第i个阶段表示处理第i个物品,第i-1个阶段表示处理第i-1个物品,则当处理第i个物品时,前i-1个物品已处理完毕,只需要考虑第i-1阶段向第i阶段的转移。

状态表示:c[i][j] 表示将前i种物品放入体积为j的背包中,获得的最大重量。

第i个物品处理状态 2种:

  1. 不放入:放入背包的重量不增加(跟第i-1个物品一样),c[i][j] = c[i-1][j]。
  2. 放入:问题可转化为,在第i-1阶段向第i阶段转化。因为第i阶段已经放入体积v[i](重量w[i]),所以第i-1阶段体积j-v[i],此时可以知道c[i][j] = c[i-1][j-v[i]] + w[i]

特殊情况:背包体积不够,则肯定不能放入物品,有c[i][j] = c[i-1][j],j < v[i]

因此,可以得到状态转移方程:
当j < v[i]时,c[i][j] = c[i-1][j]
当j >= v[i]时,c[i][j] = max(c[i-1][j], c[i-1][j-v[i]] + w[i])

而初始状态:
c[0..n][j] = 0,c[i][0..V] = 0

因此,可以通过状态转移方程得到所有c[i][j]值,而c[n][V]就是所求。
代码:

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 * 计算01背包问题的结果
 * @param V int整型 背包的体积
 * @param n int整型 物品的个数
 * @param vw int整型vector> 第一维度为n,第二维度为2的二维数组,vw[i][0],vw[i][1]分别描述i+1个物品的vi,wi
 * @return int整型
 */
int knapsack(int V, int n, vector >& vw) {
    // write code here
    //         assert(V >= 1 && V <= 1000);
    //         assert(n >= 1 && n == vw.size());
    //         assert(vw[0].size() == 2);

    vector > c(n + 1, vector(V + 1, 0)); // 全部初始化为0

    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= V; ++j) {
            int idx = i - 1;
            if (j < vw[idx][0]) {
                c[i][j] = c[i - 1][j];
            }
            else {
                c[i][j] = std::max(c[i - 1][j], c[i - 1][j - vw[idx][0]] + vw[idx][1]);
            }
        }
    }
    return c[n][V];
}