LeetCode常见题型——背包问题

1. 算法思想

背包问题是一种组合优化的NP 完全问题。 所谓NP完全问题(NP-C问题),是世界七大数学难题之一。 NP的英文全称是Non-deterministic Polynomial的问题,即多项式复杂程度的非确定性问题

背包问题:有N 个物品和容量为W 的背包,每个物品都有自己的体积w 和价值v,求拿哪些物品可以使得背包所装下物品的总价值最大。

  • 如果限定每种物品只能选择0 个或1 个,则问题称为0-1 背包问题;
  • 如果不限定每种物品的数量,则问题称为无界背包问题或完全背包问题。

可以用动态规划来解决背包问题。

以0-1 背包问题为例。我们可以定义一个二维数组dp存储最大价值,其中dp[i][j] 表示前i 件物品体积不超过j 的情况下能达到的最大价值。在我们遍历到第i 件物品时,在当前背包总容量为j 的情况下,如果我们不将物品i 放入背包,那么dp[i][j] = dp[i-1][j],即前i 个物品的最大价值等于只取前i-1 个物品时的最大价值;如果我们将物品i 放入背包,假设第i 件物品体积为w,价值为v,那么我们得到dp[i][j] = dp[i-1][j-w] + v。我们只需在遍历过程中对这两种情况取最大值即可,总时间复杂度和空间复杂度都为O(NW)。

int knapsack(vector<int> weights, vector<int> values, int N, int W) {
    vector<vector<int>> dp(N + 1, vector<int>(W + 1, 0));
    for (int i = 1; i <= N; ++i) {
        int w = weights[i-1], v = values[i-1];
        for (int j = 1; j <= W; ++j) {
            if (j >= w) {
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-w] + v);
            } else {
                dp[i][j] = dp[i-1][j];
            }
        }
    }
    return dp[N][W];
}

我们可以进一步对0-1 背包进行空间优化,将空间复杂度降低为O(W)。我们可以去掉dp 矩阵的第一个维度,在考虑物品i 时变成dp[j] = max(dp[j], dp[j-w] + v)。这里要注意我们在遍历每一行的时候必须逆向遍历,这样才能够调用上一行物品i-1 时dp[j-w] 的值;若按照从左往右的顺序进行正向遍历,则dp[j-w] 的值在遍历到j 之前就已经被更新成物品i 的值了。

int knapsack(vector<int> weights, vector<int> values, int N, int W) {
    vector<int> dp(W + 1, 0);
    for (int i = 1; i <= N; ++i) {
        int w = weights[i-1], v = values[i-1];
        for (int j = W; j >= w; --j) {
            dp[j] = max(dp[j], dp[j-w] + v);
        }
    }
    return dp[W];
}

在完全背包问题中,一个物品可以拿多次。如果仍采用这种方法,假设背包容量无穷大而物体的体积无穷小,我们这里的比较次数也会趋近于无穷大,远超O(NW) 的时间复杂度。因此,对于拿多个物品的情况,我们就得到了完全背包问题的状态转移方程:dp[i][j] = max(dp[i-1][j], dp[i][j-w] + v),其与0-1 背包问题的差别仅仅是把状态转移方程中的第二个i-1 变成了i。

int knapsack(vector<int> weights, vector<int> values, int N, int W) {
    vector<vector<int>> dp(N + 1, vector<int>(W + 1, 0));
    for (int i = 1; i <= N; ++i) {
        int w = weights[i-1], v = values[i-1];
        for (int j = 1; j <= W; ++j) {
            if (j >= w) {
                dp[i][j] = max(dp[i-1][j], dp[i][j-w] + v);
            } else {
                dp[i][j] = dp[i-1][j];
            }
        }
    }
    return dp[N][W];
}

同样的,我们也可以利用空间压缩将时间复杂度降低为O¹Wº。这里要注意我们在遍历每一
行的时候必须正向遍历,因为我们需要利用当前物品在第j-w 列的信息。

int knapsack(vector<int> weights, vector<int> values, int N, int W) {
    vector<int> dp(W + 1, 0);
    for (int i = 1; i <= N; ++i) {
        int w = weights[i-1], v = values[i-1];
        for (int j = w; j <= W; ++j) {
            dp[j] = max(dp[j], dp[j-w] + v);
        }
    }
    return dp[W];
}

2. 常见题型

LeetCode-416. Partition Equal Subset Sum [C++][Java]_贫道绝缘子的博客-CSDN博客Given anon-emptyarraynumscontainingonly positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.https://blog.csdn.net/qq_15711195/article/details/123451917LeetCode-474. Ones and Zeroes [C++][Java]_贫道绝缘子的博客-CSDN博客You are given an array of binary stringsstrsand two integersmandn. Returnthe size of the largest subset ofstrssuch that there areat mostm0's andn1's in the subset. A setxis asubsetof a setyif all elements ofxare also elements ofy.https://blog.csdn.net/qq_15711195/article/details/123454650LeetCode-322. Coin Change [C++][Java]_贫道绝缘子的博客-CSDN博客Returnthe fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return-1.https://blog.csdn.net/qq_15711195/article/details/123455088

LeetCode-494. Target Sum [C++][Java]_贫道绝缘子的博客-CSDN博客You are given an integer arraynumsand an integertarget. You want to build anexpressionout of nums by adding one of the symbols'+'and'-'before each integer in nums and then concatenate all the integers.https://blog.csdn.net/qq_15711195/article/details/123491394

参考文献

【1】算法基础:NP完全问题_清水河C罗——Leonardo-Liu-CSDN博客_np完全问题

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贫道绝缘子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值