完全背包问题

完全背包问题

文章目录

    • 完全背包问题
      • 一、完全背包问题和01背包问题的区别
        • 0/1 背包问题(0/1 Knapsack Problem)
        • 完全背包问题(Complete Knapsack Problem)
        • 总结区别
      • 二、本例子中背包问题的描述
      • 三、动态规划思路
        • 定义状态
        • 初始化状态
        • 状态转移方程
        • 填充状态表格
        • 获取最优解
      • 四、二维数组代码
      • 五、一维数组


一、完全背包问题和01背包问题的区别

0/1 背包问题(0/1 Knapsack Problem)

  1. 物品的选择限制

    • 在 0/1 背包问题中,每种物品要么被选择放入背包,要么不被选择放入,即每个物品最多只能选一次(0表示不选,1表示选)。
  2. 限制条件

    • 0/1 背包问题通常具有限制条件,其中包括一个固定的背包容量限制(背包的最大容量),通常用一个正整数 W 表示。
    • 问题的目标是选择物品,使得它们的总重量不超过背包容量限制 W,同时总价值最大化。
  3. 动态规划状态定义

    • 在 0/1 背包问题中,通常使用一个二维数组 dp[i][j] 来表示前 i 个物品在背包容量为 j 时的最大价值。
    • 状态转移方程通常如下:dp[i][j] = max(dp[i-1][j], dp[i-1][j - weight[i]] + value[i]),表示在考虑第 i 个物品时,可以选择放入或不放入背包,取两者中的最大值。

完全背包问题(Complete Knapsack Problem)

  1. 物品的选择限制

    • 在完全背包问题中,每种物品可以选择放入背包的次数是无限的,即每个物品可以被选择放入背包多次。
  2. 限制条件

    • 完全背包问题同样具有一个固定的背包容量限制 W,但每个物品可以选择放入多次,不像 0/1 背包中每个物品只能选一次。
  3. 动态规划状态定义

    • 在完全背包问题中,同样使用一个二维数组 dp[i][j] 来表示前 i 个物品在背包容量为 j 时的最大价值。
    • 不同的是,状态转移方程稍有不同,通常如下:dp[i][j] = max(dp[i-1][j], dp[i][j - weight[i]] + value[i]),表示在考虑第 i 个物品时,可以选择放入或不放入背包,而且可以多次选择放入物品 i,取两者中的最大值。

总结区别

  • 0/1 背包问题中,每个物品要么选中要么不选中,而在完全背包问题中,每个物品可以选择放入多次。
  • 0/1 背包问题的限制条件中,每个物品只能被选中一次,而完全背包问题中,每个物品可以被选中多次。
  • 解决 0/1 背包问题和完全背包问题的动态规划算法的状态转移方程略有不同,主要是在计算放入物品时的逻辑上有差异。

二、本例子中背包问题的描述

在这个例子中,我们有三个物品,它们的重量分别为1、3、4,对应的价值为15、20、30。背包的容量限制为4,每个物品可以选择放入多次。

三、动态规划思路

  1. 定义状态

首先,我们需要定义一个状态,以便表示问题的子问题和最优解。在这个问题中,我们可以使用二维数组 dp[i][j] 来表示前i个物品中,在背包容量为j时所能获得的最大价值。其中,i表示物品的数量,j表示背包的容量。

  1. 初始化状态

我们需要初始化状态数组 dp,和0/1背包初始化方式不同的是,完全背包中只有 dp[0][j] 应该初始化为0,因为没有物品可供选择;第一行是不需要进行初始化的,因为第一个物品可以放入多次,遍历后面的物品时使用相同的步骤即可。

  1. 状态转移方程

接下来,我们需要找到状态之间的转移关系,即如何从子问题的最优解推导出原问题的最优解。在这个问题中状态转移方程如下:

  • 如果当前背包容量 j 小于物品 i 的重量 weight[i],则无法将物品 i 放入背包,此时 dp[i][j] 等于上一个状态 dp[i-1][j] 的值。
  • 如果 j 大于等于 weight[i],我们可以选择放入物品 i 或不放入物品 i。因此,dp[i][j] 取两者中的最大值:
    • 不放入物品 i,即 dp[i-1][j]
    • 放入物品 i,即 dp[i][j - weight[i]] + value[i],这里的 dp[i][j - weight[i]] 表示在考虑物品 i 时,剩余背包容量 j - weight[i] 的最优解,加上物品 i 的价值 value[i](0/1背包中,因为不能使用同一种物品,所以是dp[i-1][j-weight[i]]+ value[i])。
  1. 填充状态表格

通过上述状态转移方程,我们可以通过双重循环遍历所有的子问题,从而填充状态表格 dp。外层循环遍历物品 i,内层循环遍历背包容量 j,根据状态转移方程更新 dp[i][j]

  1. 获取最优解

最后,我们可以通过 dp[weight.size() - 1][bagweight] 来获取问题的最优解,即在考虑所有物品并且背包容量为 bagweight 时的最大价值。

四、二维数组代码

  1. 首先,定义三个关键数组:

    • weight:物品的重量数组。
    • value:物品的价值数组。
    • dp:动态规划状态表格,dp[i][j] 表示前i个物品在背包容量为j时的最大价值。
  2. for 循环中,遍历每一个物品 i,以及每一个可能的背包容量 j遍历顺序可以更换

  3. 在内部的 if-else 条件语句中,你根据状态转移方程更新 dp[i][j]

    • 如果 j 小于物品 i 的重量 weight[i],则无法放入物品 i,所以 dp[i][j] 等于上一个状态 dp[i-1][j](表示不放入物品 i)。
    • 否则,选择放入物品 i 或不放入物品 i,取两者中的最大值,这样就保证了在背包容量为 j 时的最大价值。
    #include 
    #include 
    using namespace std;
    
    void completePack()
    {
        vector<int> weight = { 1, 3, 4 };
        vector<int> value = { 15, 20, 30 };
        int bagweight = 4;
        vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));
    
        for (int i = 0; i < weight.size(); i++) {
            for (int j = 0; j <= bagweight; j++) {
                if (j < weight[i]) {
                    // 当前背包容量不足以放入物品 i
                    dp[i][j] = (i > 0) ? dp[i - 1][j] : 0;
                }
                else {
                    // 选择放入物品 i 或者不放入
                    dp[i][j] = max((i > 0) ? dp[i - 1][j] : 0, dp[i][j - weight[i]] + value[i]);
                }
            }
        }
    
        // dp[weight.size() - 1][bagweight] 即为问题的最优解
        cout << "Maximum value: " << dp[weight.size() - 1][bagweight] << endl;
    }
    
    int main()
    {
        completePack();
        return 0;
    }
    

五、一维数组

void CompletePack() {
    vector<int> weight = {1, 3, 4};
    vector<int> value = {15, 20, 30};
    int bagWeight = 4;
    vector<int> dp(bagWeight + 1, 0);
    for(int i = 0; i < weight.size(); i++) { // 遍历物品
        for(int j = weight[i]; j <= bagWeight; j++) { // 遍历背包容量
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    cout << dp[bagWeight] << endl;
}
int main() {
    CompletePack();
}
void CompletePack() {
    vector<int> weight = {1, 3, 4};
    vector<int> value = {15, 20, 30};
    int bagWeight = 4;

    vector<int> dp(bagWeight + 1, 0);

    for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量,注意这里j不可以直接初始化成weight[i],因为第一个物品就有可能大于bagWeight
        for(int i = 0; i < weight.size(); i++) { // 遍历物品
            if (j - weight[i] >= 0) dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    cout << dp[bagWeight] << endl;
}
int main() {
    CompletePack();
}

你可能感兴趣的:(LeetCode刷题,算法导论阅读,LeetCode,算法,动态规划)