【动态规划】0-1背包问题

概述

0-1背包问题是一种经典的动态规划问题,它的基本形式是:有一个背包,容量为 C C C,有 n n n 个物品 i i i,每个物品 i i i 的重量为 w i w_i wi,价值为 v i v_i vi。现在要从这 n n n 个物品中选出若干个放入背包中,使得背包中物品的总重量不超过 C C C,且物品的总价值最大。

解题思路

这个问题可以用动态规划来解决,一般使用一个二维数组 d p dp dp 来存储最大价值。

d p [ i ] [ j ] dp[i][j] dp[i][j] 表示在前 i i i 个物品中选择若干个物品,恰好放入容量为 j j j 的背包中,可以获得的最大价值。

那么对于每个物品,我们可以选择将其放入背包中,也可以选择不放入。

如果选择将第 i i i 个物品放入背包中,则有 d p [ i ] [ j ] = d p [ i − 1 ] [ j − w i ] + v i dp[i][j] = dp[i-1][j-w_i] + v_i dp[i][j]=dp[i1][jwi]+vi

如果选择不放入,则有 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i1][j]。我们需要取这两种情况的较大值作为 d p [ i ] [ j ] dp[i][j] dp[i][j] 的值。

可以得到状态转移方程如下:

dp[i][j]=max⁡(dp[i−1][j−wi]+vi,dp[i−1][j])

最后,最大价值就是 d p [ n ] [ C ] dp[n][C] dp[n][C]

图表分析

书包重量(公斤) 0 1 2 3 4 5 6 7 8
物品不存在 0 0 0 0 0 0 0 0 0
物品1:6公斤、48元 0 0 0 0 0 0 48 48 48
物品2:1公斤、7元 0 7 7 7 7 7 48 48 48
物品3:5公斤、40元 0 7 7 7 7 40 48 55 55
物品4:2公斤、12元 0 7 12 19 19 40 48 55 60
物品5:1公斤、8元 0 8 15 20 27 40 48 56 63

当物品不存在和书包重量为0的时候,背包价值肯定也为0。

切记:dp[i][j]表示前i个物品、背包承重为j时的最大价值。

前一件物品:只有当背包容量为6的时空才可以装下,所以dp[1][6]=48。由于这是选择的前一件物品,那么在背包容量为7和8时,不会考虑后面的物品,值还是48。

前两件物品:当背包容量为1时,可以装下第二件物品,这时背包价值为7,dp[2][1] = 7。一直到书包承重5公斤时,都只能装下物品2,dp[2][1]到dp[2][5]都是等于7。 书包承重6公斤时,可以装物品1或物品2,两者只能装1个,通过对比物品1和物品2的价值大小,我们选择物品1,即dp[2][6]=48。书包承重7公斤时,可以同时装物品1、物品2,此时dp[2][7]=物品2的价值 + 物品1的价值=55。dp[2][8]也是同样的道理。

前三件物品:背包容量从1到4时,只能装下物品2,即最大价值为7。当背包容量为5时,可以装下物品3,价值为40,当背包容量为6时,只装物品1,背包价值为48。

以此类推,可得到五件物品的情况下,背包能装下且得到的最大价值是多少。

示例代码

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int knapsack(vector<int>& weights, vector<int>& values, int capacity) {
    
    
    int n = weights.size();
    vector<vector<int>> dp(n + 1, vector<int>(capacity + 1, 0));

    for (int i = 1; i <= n; i++) {
    
    
        for (int j = 1; j <= capacity; j++) {
    
    
            if (weights[i - 1] > j) {
    
    
                // 当前物品重量大于背包容量,无法放入,继承上一个最优解
                dp[i][j] = dp[i - 1][j];
            } else {
    
    
                // 当前物品可以选择放入或者不放入
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1]);
            }
        }
    }

    return dp[n][capacity];
}

int main() {
    
    
	vector<int> weights = {
    
     6, 1, 5, 2, 1 };
	vector<int> values = {
    
     48, 7, 40, 12, 8 };
    int capacity = 8;

    int result = knapsack(weights, values, capacity);
    cout << "Max value: " << result << endl;

    return 0;
}

运行结果如下所示:
在这里插入图片描述

解析:
首先定义了函数knapsack,该函数接受三个参数:物品重量的数组weights、物品价值的数组values和背包容量capacity。函数返回值为能够装入背包的最大价值。

函数内部,首先获取物品的数量n,然后定义了一个dp数组来保存中间计算结果。其中,dp[i][j]表示前i个物品,容量为j时,能够获得的最大价值。

接下来使用两层循环,遍历每一个可能的状态。对于每个状态,如果当前物品重量大于背包容量,则无法放入背包中,继承上一个最优解。否则,当前物品可以选择放入或者不放入背包中。这里使用了max函数来求解当前状态下的最大值。最后返回dp[n][capacity]即可。

在main函数中,定义了物品重量、价值和背包容量的数组,并调用knapsack函数求解最大价值,并输出结果。

总结

0-1背包问题是一个经典的动态规划问题,求解的是一个有限容量的背包所能携带的最大价值。其特点是:每种物品只有一个,可以选择装或不装,不能部分装入。

0-1背包问题可以用动态规划来解决。具体思路如下:

  1. 定义状态:定义 dp[i][j] 表示前 i 个物品放入容量为 j 的背包中所能获得的最大价值。

  2. 初始化状态:dp[0][j] 和 dp[i][0] 都初始化为 0,因为当没有物品或者背包容量为 0 时,所能获得的最大价值为 0。

  3. 状态转移方程:当当前物品重量大于当前背包容量时,无法放入,当前最优解为继承上一个最优解;否则,当前物品可以选择放入或者不放入,取决于两种情况下的最大价值。dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1]);

  4. 返回结果:dp[n][capacity] 表示前 n 个物品放入容量为 capacity 的背包中所能获得的最大价值。

代码实现时,可以使用二维数组 dp[i][j] 来存储状态转移方程的结果,其中 i 表示物品的编号,j 表示背包的容量。初始化时,将 dp[0][j] 和 dp[i][0] 都初始化为 0。最终结果为 dp[n][capacity]。时间复杂度为 O(ncapacity),空间复杂度为 O(ncapacity)。

猜你喜欢

转载自blog.csdn.net/qq_44878985/article/details/130656561