完全背包

求取 dp14 有两种情况:

  1. 放物品1
  2. 还是不放物品1

如果不放物品1, 那么背包的价值应该是 dp04 即 容量为4的背包,只放物品0的情况。

推导方向如图:

如果放物品1, 那么背包要先留出物品1的容量,目前容量是4,物品1 的容量(就是物品1的重量)为3,此时背包剩下容量为1。

容量为1,只考虑放物品0 和物品1 的最大价值是 dp11注意 这里和 01背包理论基础(二维数组) 有所不同了 !一个是(i-1)意义是放了i这个后,剩下i-1的最大值。完全背包不用减1,放了i之后依旧是0-i的最大值

01背包理论基础(二维数组) 中,背包先空留出物品1的容量,此时容量为1,只考虑放物品0的最大价值是 dp01因为01背包每个物品只有一个,既然空出物品1,那背包中也不会再有物品1

而在完全背包中,物品是可以放无限个,所以 即使空出物品1空间重量,那背包中也可能还有物品1,所以此时我们依然考虑放 物品0 和 物品1 的最大价值即: dp11, 而不是 dp01

所以 放物品1 的情况 = dp11 + 物品1 的价值,推导方向如图:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,bagweight;
    cin>>n>>bagweight;
    vector<int>weight(n,0);
    vector<int>value(n,0);
    for(int i=0;i<n;i++){
        cin>>weight[i]>>value[i];
    }
    vector<vector<int>>dp(n,vector<int>(bagweight+1,0));
    for(int i=0;i<n;i++){
        dp[i][0]=0;
    }
    for (int j = weight[0]; j <= bagweight; j++){
    dp[0][j] = dp[0][j - weight[0]] + value[0];
    }
    for(int i=1;i<n;i++){
        for(int j=1;j<=bagweight;j++){
            if(weight[i]>j)dp[i][j]=dp[i-1][j];//不要忘记
            else dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
            
        }
    }
    cout<<dp[n-1][bagweight];

}

零钱兑换2:

二维:

cpp 复制代码
class Solution {
public:
    int change(int amount, vector<int>& coins) {
        //dp[i][j]用前0-i个物品装满amout大小的背包,有多少种方法
        vector<vector<unsigned long long>>dp(coins.size(),vector<unsigned long long>(amount+1,0));
        if(coins[0]>amount)return 1;
        for(int i=0;i<coins.size();i++){
            dp[i][0]=1;}
        for (int j = 0; j <= amount; j++) {
             if (j % coins[0] == 0) dp[0][j] = 1;
        }
        for(int i=1;i<coins.size();i++){
            for(int j=1;j<=amount;j++){
                if(coins[i]>j)dp[i][j]=dp[i-1][j];
                else dp[i][j]=dp[i-1][j]+dp[i][j-coins[i]];//相加,不是max
            }
        }
        return dp[coins.size()-1][amount];
    }
};

一维:

dp[j] += dp[j - coins[i]]

怎么理解这个式子?

题目设定

硬币 coins = [1, 2],目标总金额 amount = 4

(最终答案应该是 3 种组合:[1,1,1,1][1,1,2][2,2]

我们一步步执行代码 dp[j] += dp[j - coins[i]],盯着 dp[4] 这个格子,看它到底是怎么被"新增"出来的。


🟦 初始状态

dp[0] = 1(凑0元有一种方法:啥也不拿)。

其他 dp[1]dp[4] 全是 0。

金额 j 0 1 2 3 4
dpj 1 0 0 0 0

🟩 第一轮:处理硬币 1 (面值 1)

(正序循环,j 从 1 到 4)

  1. j=1dp[1] += dp[0]dp[1] = 0 + 1 = 1。(凑1的方案:[1]

  2. j=2dp[2] += dp[1]dp[2] = 0 + 1 = 1。(凑2的方案:[1,1]

  3. j=3dp[3] += dp[2]dp[3] = 0 + 1 = 1。(凑3的方案:[1,1,1]

  4. j=4dp[4] += dp[3]dp[4] = 0 + 1 = 1。(凑4的方案:[1,1,1,1]

第一轮结束后的表格:

金额 j 0 1 2 3 4
dpj 1 1 1 1 1

此时,dp[4] 里的这 1 种方法,是只用硬币 1 凑出来的老方法 [1,1,1,1]


🟧 第二轮:处理硬币 2 (面值 2)

(这是最关键的一步!正序循环,j 从 2 到 4)

① 计算 j = 2

dp[2] = dp[2] + dp[0] = 1 + 1 = 2

  • 左边的旧 dp[2](不选) :是 1,代表旧方案 [1,1]

  • 右边的新增 dp[0](选) :是 1,代表剩余0元的方法(空集)+ 硬币2 = 新方案 [2]

  • 结果 :凑2现在有 2 种方法:[1,1][2]

金额 j 0 1 2 3 4
dpj 1 1 2 1 1

② 计算 j = 3

dp[3] = dp[3] + dp[1] = 1 + 1 = 2

  • 左边的旧 dp[3](不选) :是 1,代表旧方案 [1,1,1]

  • 右边的新增 dp[1](选)dp[1] 此时是 1 (来自上一轮,没被硬币2污染,因为 1 < 2)。它代表剩余1元的方法 [1] + 硬币2 = 新方案 [1,2]

  • 结果 :凑3现在有 2 种方法:[1,1,1][1,2](注意顺序固定,没有 [2,1])。

金额 j 0 1 2 3 4
dpj 1 1 2 2 1

③ 计算 j = 4(你困惑的焦点!):

dp[4] = dp[4] + dp[2] = 1 + 2 = 3

我们把左右拆开看:

  • 左边的旧 dp[4](不选硬币2)

    值是 1。它代表原来凑成 4 的老方法 ,即上一轮留下的 [1,1,1,1]。这些方法不动,继续保留。

  • 右边的新增 dp[2](选硬币2)

    注意! 这里的 dp[2] 不是上一轮的 1,而是当前这一轮刚刚算出来的 2

    2 代表着凑成剩余金额 22 种方法

    • 方法A:[1,1] + 硬币2 = 得到新方案 [1,1,2]

    • 方法B:[2] + 硬币2 = 得到新方案 [2,2]

你看dp[4] 原来是 [1,1,1,1](1种)。

通过 += dp[2],我们新增[1,1,2][2,2](2种)。

所以总数变成了 1 + 2 = 3 种。

金额 j 0 1 2 3 4
dpj 1 1 2 2 3
相关推荐
Smoothcloud润云2 小时前
GPU租赁数据安全怎么做?
人工智能·算法·ai·aigc·gpu算力·gpu
呜喵王阿尔萨斯2 小时前
extern “C“ in C++
c语言·c++·算法
乐观勇敢坚强的老彭2 小时前
C++信奥静态数组和动态数组
数据结构·c++·算法
月光船幽幽3 小时前
呼吸孔设计重塑系统稳定性
python·科技·算法
旖旎夜光3 小时前
LeetCode 991: 坏了的计算器(贪心算法) —— 题解
数据结构·c++·算法·leetcode·贪心算法
旖旎夜光3 小时前
LeetCode 553:最优除法(贪心算法) —— 题解
数据结构·c++·算法·leetcode·贪心算法
雾喔4 小时前
算法练习7
java·数据结构·算法
过期的秋刀鱼!4 小时前
重点-偏差方差与神经网络
人工智能·深度学习·神经网络·算法·机器学习·过拟合·l2正则化
可编程芯片开发5 小时前
基于模糊PID控制的六步逆变器供电无刷直流电机调速simulink仿真
算法