【Day33 LeetCode】动态规划DP Ⅵ 背包问题

news/2025/2/8 22:57:54 标签: leetcode, 动态规划, 算法

一、动态规划DP Ⅵ 背包问题

1、leetcode.cn/problems/coin-change/description/" rel="nofollow">零钱兑换 322

本质是完全背包问题,以最终的金额数amount为背包容量,不同面额的硬币为物品,物品数量无数,求装满物品需要的最少物品数。直接套用完全背包模板。由于求最少物品数是在 装满物品的方法里寻找,那么是寻找装满物品的组合 还是 排列呢?组合,因为最少物品数与顺序无关,所以采用组合即可。其实采用排列也是可以的,因为都是在结果里找到最少物品数,只是排列的结果范围更多。
下面是组合的代码:先遍历物品,再遍历背包

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount + 1, amount + 1);
        dp[0] = 0;
        for(int coin : coins)  // 先遍历物品
            for(int i=coin; i<=amount; ++i) // 再遍历背包
                dp[i] = min(dp[i], dp[i-coin] + 1);
        return dp[amount] < amount + 1 ? dp[amount] : -1;
    }
};

下面是排列的代码:先遍历背包,再遍历物品

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1, amount+1);
        dp[0] = 0;
        for(int i=1; i<=amount; ++i)
            for(int coin : coins)
                if(i>=coin)
                    dp[i] = min(dp[i], dp[i-coin]+1);
        return dp[amount] < amount+1 ? dp[amount] : -1;
    }
};

2、leetcode.cn/problems/perfect-squares/description/" rel="nofollow">完全平方数 279

这题与上一题零钱兑换几乎一模一样,只不过物品这里没写明,物品变成了平方小于等于背包容量的数,代码与上面几乎一模一样。
先遍历物品,再遍历背包,代码如下:

class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1, n);
        dp[0] = 0;
        for(int i=1; i*i<=n; ++i)  // 先遍历物品
            for(int j=i*i; j<=n; ++j)  // 再遍历背包
                dp[j] = min(dp[j], dp[j-i*i]+1);
        return dp[n];
    }
};

先遍历背包,再遍历物品,代码如下:

class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1, n);
        dp[0] = 0;
        for(int i=1; i<=n; ++i)  // 先遍历背包
            for(int j=1; j*j<=n; ++j)  // 再遍历物品
                if(i - j * j >= 0)
                    dp[i] = min(dp[i], dp[i-j*j]+1);
        return dp[n];
    }
};

3、leetcode.cn/problems/word-break/description/" rel="nofollow">单词拆分 139

目标字符串需要由字典里的子字符串构成,相当于使用物品为字典里的字符串,且没有数量限制,装满容量为目标字符串长度的背包的方法有多少个,这是个完全背包问题。并且,对于结果来说,子字符串的结果讲究顺序,需要遍历所有顺序,所以先遍历背包,再遍历物品。代码如下:

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet;
        for(string word : wordDict)
            wordSet.insert(word);
        int n = s.size();
        vector<int> dp(n + 1);
        dp[0] = 1;
        for(int i=1; i<=n; ++i)  // 先遍历背包
            for(string word : wordSet)  // 再遍历物品
                if(i >= word.size() && s.substr(i-word.size(), word.size())==word)
                    dp[i] |=  dp[i-word.size()];
        return dp[n];
    }
};

4、多重背包问题

将每个同一种类的物品视为一个独立的物品,那么就将多重背包问题转换成了01背包问题,这样做想法简单直接,但是在扩容vector时比较耗时。
另一种代码实现更高效的方法时在循环里使用不同数量的物品。

# include<iostream>
# include<vector>

using namespace std;

int main(){
    int Capacity, N;
    cin >> Capacity >> N; // 容量  物品种类数
    vector<int> weights(N), values(N), amount(N); // 重量、价值、数量
    for(int i=0; i<N; ++i)  cin >> weights[i];
    for(int i=0; i<N; ++i)  cin >> values[i];
    for(int i=0; i<N; ++i)  cin >> amount[i];
    
    vector<int> dp(Capacity+1);
    for(int i=0; i<N; ++i) // 遍历物品
        for(int j=Capacity; j>=weights[i]; --j)  // 遍历背包
            for(int kk=1; kk<=amount[i] && j - kk * weights[i] >= 0; ++kk)
                dp[j] = max(dp[j], dp[j - kk * weights[i]] + kk * values[i]);
    cout << dp[Capacity] << endl;
    return 0;
}

二、写在后面

对于背包和物品的遍历顺序进一步加深了理解,掌握方法论解决背包问题就是套模板。


http://www.niftyadmin.cn/n/5845361.html

相关文章

数据库系统架构与DBMS功能探微:现代信息时代数据管理的关键

欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭&#xff5e; ??? 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。?? 希望在这里&#xff0c;我们能一起探…

《Kettle实操案例一(全量/增量更新与邮件发送)》

目录 一、场景描述:二、要求:三、思路四、整体作业五、各部分详细配置1、Start2、转换-获取执行开始时间3、获取目标表抽取前行数4、检验字段的值5、增量更新6、全量更新7、获取目标表抽取后行数8、获取执行结束时间9、日志写入数据库10、写日志11、发送数据抽取完成邮件 六、最…

c语言:取绝对值

假设我们有一个 long 类型的变量 l&#xff0c;我们希望恢复其绝对值。以下是两种方法的对比&#xff1a; 方法1&#xff1a;使用条件语句 这个很好理解&#xff0c;负数时取负运算 &#xff0c;用于数值的符号反转。 long abs_value(long l) {if (l < 0) {return -l;} e…

储能系统-系统架构

已更新系列文章包括104、61850、modbus 、单片机等&#xff0c;欢迎关注 IEC61850实现方案和测试-1-CSDN博客 快速了解104协议-CSDN博客 104调试工具2_104协议调试工具-CSDN博客 1 电池储能系统&#xff08;BESS&#xff09; 架构 电池储能系统主要包括、电池、pcs、本地控制…

RISC-V芯片与扩展医疗影像处理边缘设备编程探析

一、引言 在数智化医疗快速发展的当下,医疗影像处理作为疾病诊断、治疗方案制定的关键环节,对设备性能与效率提出了极高要求。传统的医疗影像处理多依赖于集中式的大型计算中心,数据需传输至远程服务器进行处理,这不仅面临网络延迟、带宽限制的问题,还存在数据隐私安全风险…

Vue 双向数据绑定的原理

Vue 的双向数据绑定是其核心特性之一&#xff0c;它可以让视图与数据保持同步&#xff0c;简化了开发者在 DOM 操作上的工作。Vue 的双向数据绑定通过 响应式系统 和 DOM 事件监听 来实现&#xff0c;当数据发生变化时&#xff0c;视图会自动更新&#xff1b;当视图中的元素&am…

机器学习 - 需要了解的条件概率、高斯分布、似然函数

似然函数是连接数据与参数的桥梁&#xff0c;通过“数据反推参数”的逆向思维&#xff0c;成为统计推断的核心工具。理解它的关键在于区分“参数固定时数据的概率”与“数据固定时参数的合理性”&#xff0c;这种视角转换是掌握现代统计学和机器学习的基础。 一、在学习似然函…

hive的几种复杂数据类型

Hive的几种复杂数据类型 Hive 提供了几种复杂数据类型&#xff0c;能够支持更灵活和多样的数据存储。这些复杂数据类型对于处理嵌套数据或不规则数据特别有用。主要包括以下几种&#xff1a; 文章目录 Hive的几种复杂数据类型1. 数组&#xff08;ARRAY&#xff09;2. 结构体&a…