完全背包

求取 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=1 :dp[1] += dp[0] → dp[1] = 0 + 1 = 1。(凑1的方案:[1])

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

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

  4. j=4 :dp[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 代表着凑成剩余金额 2 的 2 种方法:

    • 方法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 ✅
相关推荐
飘尘1 分钟前
从"算子"到"AI Infra":大模型背后看不见的那群人在忙什么
人工智能·算法·面试
夏幻灵40 分钟前
每日一题:LeetCode 3:无重复字符的最长子串——滑动窗口思路详解
算法·leetcode·职场和发展
CarIise1 小时前
Java实训阶段查漏补缺复习笔记2
java·linux·算法
无忧.芙桃1 小时前
C++ 并查集详解:原理、实现与应用
c语言·c++·算法·leetcode
蛋蛋的就会顺顺的1 小时前
hot100——二叉树
java·数据结构·算法·leetcode·力扣
Code_Solitude2 小时前
c语言:二分法的完整补充
c语言·开发语言·算法
一切皆是因缘际会2 小时前
掌控信息论:同源星际通信基础理论
人工智能·算法·ai
动词ing4 小时前
【C语言题目练习】算法 整数反转
c语言·开发语言·算法
Nil2084 小时前
leetcode 55跳跃游戏
算法·leetcode·游戏
longlongzihan4 小时前
LeetCode 128. 最长连续序列 —— 从排序到哈希集合
数据结构·c++·算法·leetcode·排序算法·哈希表