当前位置: 首页 > article >正文

动态规划day38|322. 零钱兑换(背包满了吗?最小值怎么表示?)、279. 完全平方数、139. 单词拆分、多重背包要点、背包问题大总结

动态规划day38|322. 零钱兑换(背包满了吗?最小值怎么表示?)、279. 完全平方数、139. 单词拆分、多重背包要点、背包问题大总结

  • 322. 零钱兑换
  • 279. 完全平方数
  • 139. 单词拆分
  • 多重背包要点
  • 背包问题大总结

322. 零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1

示例 2:

输入:coins = [2], amount = 3
输出:-1

示例 3:

输入:coins = [1], amount = 0
输出:0

提示:

  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 231 - 1
  • 0 <= amount <= 104

第一次做的时候,我不知道该怎么去表示背包已经满了以及什么才是最小值

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1,INT_MAX);
        dp[0]=0;
        for(int i=0;i<coins.size();i++)
            for(int j=coins[i];j<=amount;j++)
                if(dp[j-coins[i]]!=INT_MAX)
                dp[j]=min(dp[j-coins[i]]+1,dp[j]);

        if(dp[amount]==INT_MAX) return -1;
        return dp[amount];
    }
};

首先回答我一开始的两个疑问:

  • 怎么去表示背包已经满了?
    首先我们整体过一遍背包的过程:对于第一个物品,从dp[coins[i]]开始,它是在dp[0]的基础上加的,所以它必然是满的,然后之后一个背包就是在dp[coins[i]]的基础上加的,所以它也是满的……就这样不断迭代推进,像多米诺骨牌一样,要不就装不了,要不就是装满的。如果从中间看确实难看出来,但是从开头的几个,如当i=0时的dp[1]、dp[2],就明确的知道:只要dp[j]有值,那么它就是满的
  • 什么才是最小值?
    这就是递归函数的作用,一般最大值要有max,最小值要有min,求个数问题则是求和
    为什么这样(dp[j]=min(dp[j-coins[i]]+1,dp[j])) 就是最小值?我们还是一样,从开头开始看,当新物品纳入的时候,我们就要考虑是加入新物品“好”,还是不加新物品“好”,每回都是选两者中最小的,那么到了后面,自然而然就是最小值了。

易错点

  • 初始化:由于是求min,所以必须初始化为INT_MAX,绝不能简单的赋为0,否则0会覆盖真正的值。
if(dp[j-coins[i]]!=INT_MAX)
  dp[j]=min(dp[j-coins[i]]+1,dp[j]);

这里的if条件不可少,如果没有它,那么dp[j-coins[i]]+1就会超过INT_MAX,从而引发异常

  if(dp[amount]==INT_MAX) return -1;

当不能凑出amout的时候,dp[amount]的值还是原来初始化的值。至于为什么,你可以借助开头的几个dp来理解。

279. 完全平方数

给你一个整数 n ,返回 和为 n 的完全平方数的最少数量

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,14916 都是完全平方数,而 311 不是。

示例 1:

输入:n = 12
输出:3
解释:12 = 4 + 4 + 4

示例 2:

输入:n = 13
输出:2
解释:13 = 4 + 9

提示:

  • 1 <= n <= 104
class Solution {
public:
    vector<int> pingFangShu(int n)
    {
        vector<int> nums(n);
        for(int i=1;;i++)
        {
            if(i*i<=n)
            nums.push_back(i*i);
            else
            break;
        }
        return nums;
    }
    int numSquares(int n) {
        vector<int> dp(n+1,INT_MAX);
        dp[0]=0;
        vector<int> nums=pingFangShu(n);
        for(int i=0;i<nums.size();i++)
            for(int j=nums[i];j<=n;j++)
                if(dp[j-nums[i]]!=INT_MAX)
                dp[j]=min(dp[j-nums[i]]+1,dp[j]); 
        return dp[n];
    }
};

这样的写法是可以的,但是超出了力扣的时间限制。上面的是单独生成“物品”,选择只好在for循环里面边遍历边生成了,代码如下:

class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n+1,INT_MAX);
        dp[0]=0;
        for(int i=1;i*i<=n;i++)
            for(int j=i*i;j<=n;j++)
                if(dp[j-i*i]!=INT_MAX)
                dp[j]=min(dp[j-i*i]+1,dp[j]); 
        return dp[n];
    }
};

原理和322基本相同。

139. 单词拆分

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true

**注意:**不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

示例 1:

输入: s = “leetcode”, wordDict = [“leet”, “code”]
输出: true
解释: 返回 true 因为 “leetcode” 可以由 “leet” 和 “code” 拼接成。

示例 2:

输入: s = “applepenapple”, wordDict = [“apple”, “pen”]
输出: true
解释: 返回 true 因为 “applepenapple” 可以由 “apple” “pen” “apple” 拼接成。
注意,你可以重复使用字典中的单词。

示例 3:

输入: s = “catsandog”, wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
输出: false

提示:

  • 1 <= s.length <= 300
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 20
  • swordDict[i] 仅由小写英文字母组成
  • wordDict 中的所有字符串 互不相同
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
        vector<bool> dp(s.size() + 1, false);
        dp[0] = true;
        for (int j = 1; j <= s.size(); j++) {   // 遍历背包
            for (int i = 0; i < j; i++) {       // 遍历物品
                string word = s.substr(i, j - i); //substr(起始位置,截取的个数)
                if (wordSet.find(word) != wordSet.end() && dp[i]) {
                    dp[j] = true;
                }
            }
        }
        return dp[s.size()];
    }
};

本题难度较高,一刷可放过

多重背包要点

  • 有N种物品和一个容量为W 的背包。第 i 种物品最多有 Mi 件可用,每件耗费的空间是Ci ,价值是 Vi ,求价值总和最大。
  • **多重背包和01背包是非常像的:**每件物品最多有Mi件可用,把Mi件摊开,其实就是一个01背包问题了,即:
for (int i = 0; i < n; i++) {
        while (nums[i] > 1) { // 物品数量不是一的,都展开
            weight.push_back(weight[i]);
            value.push_back(value[i]);
            nums[i]--;
        }
    }
 
    vector<int> dp(bagWeight + 1, 0);
    for(int i = 0; i < weight.size(); i++) { // 遍历物品,注意此时的物品数量不是n
        for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    return dp[bagWeight] 
}
  • 多重背包在面试中基本不会出现

背包问题大总结

img

(这个图是代码随想录知识星球成员海螺人所画)


http://www.kler.cn/news/319430.html

相关文章:

  • 網路本地連接沒有有效的IP配置:原因與解決方法
  • 匈牙利算法详解与实现
  • 【Tomcat】常见面试题整理 共34题
  • 跨站请求伪造(CSRF)漏洞详解
  • 【MySQL】知识总结——索引的类型分类和性质
  • 2023国赛C题 蔬菜类商品的自动定价与补货决策(上)
  • Spring Boot 中实现动态列导入讲解和案例示范
  • element plus上传文件 点击确认后文件上传到接口
  • Java项目实战II基于Java+Spring Boot+MySQL的车辆管理系统(开发文档+源码+数据库)
  • 【Java】将一个List拆分使用线程池多线程运行
  • linux进程间通信——消息队列、信号量、ipc设计原理
  • 梧桐数据库(WuTongDB):向量化查询优化器的一些实现细节
  • 傅里叶变换及其应用笔记
  • 使用dom-to-image截图html区域为一张图
  • Redis --- redis事务和分布式事务锁
  • 全栈杂谈第三期 我们用的网络协议是什么
  • 前端css样式覆盖
  • 我的AI工具箱Tauri版-MicrosoftTTS文本转语音
  • 24.9.23学习笔记
  • “永辉优品”会是中国零售的答案吗?
  • 通信工程学习:什么是WLAN无线局域网
  • Python 从入门到实战28(文件的读操作)
  • 从王卫在全球可持续交通高峰论坛上的发言,透视顺丰的变革逻辑
  • [Unity Demo]从零开始制作空洞骑士Hollow Knight第十集:制作后坐力系统Recoil和小骑士的生命系统和受伤系统
  • docker容器安装nginx
  • 如何在 Linux 中管理和清理日志文件( `find` 命令按时间批量删除日志)
  • Unity DOTS系列之Struct Change核心机制分析
  • 大模型-模型预训练-训练过程优化配置
  • 【鸿蒙HarmonyOS NEXT】数据存储之分布式键值数据库
  • 克里金插值算法文件