算法训练营DAY45|322. 零钱兑换、279.完全平方数

news/2025/1/16 4:03:55/

两道题思路上有相似之处,都是求得最少的种类方法,也就是说在完全背包里给定容量时,用最少的物品去装满背包。它和用最多的方法去装满背包也有一些相似,也就是说两者实际上是互通的。

322. 零钱兑换 - 力扣(LeetCode)icon-default.png?t=MBR7https://leetcode.cn/problems/coin-change/用最少的纸张数量来凑齐目标数target,在第一次做这道题时候,很多朋友都感觉应该是模拟钱相加得到target,然后用计数器计数什么时候达到最少的纸币,很自然的想到应该要给数组排序,大数额钱放在前面,然后再用次大数额,以此类推,至少我一开始是这样想的。这样或许也是可以做出来的,不过我们本期要讲的是用完全背包的思路解答,我们来看动规五部曲分析,来体会动态规划的神奇之处。

dp数组的含义:dp【j】,j容量下对应的最少硬币个数

递推公式:递推公式和求取填满背包最多物品个数有几个,差不太多。我们是求最少那就是

dp【j】=min(dp【j】,dp【j-coins【i】】+1)

这里为什么要+1?原因在于我们并非模拟硬币的价值相加等于target,dp数组的含义就是此背包容量下,能承载的最少硬币个数有多少,我们比较当前背包容量的已有值的硬币个数与该容量减当前要遍历的硬币价值(也就是腾出背包空间,这一点不懂翻看前面的文章)+1,看它们谁更小取谁。推到最后得到的dp【target】就是我们要的值,即当前容量下我们可以取的最小硬币个数。看到这里,可能会有一个疑问,如何判断出来我们这个目标值是否真的被这些硬币数量填满了!我们并不是真的模拟硬币相加等于target,那我们是怎么知道这些硬币肯定能装满背包的呢?答案就在递推公式里,我们求大容量背包时,是由之前的小容量背包填充,也就是说在遍历target这么大的背包之前,一定有一个最小的背包已经被装满,它们可以是dp【1也可以是dp【2】这是由硬币数组来决定的。我们得到了这个之后,推较大背包容量时,min的第二个值,dp【j-coins【i】】可以帮助我们找到已经填满的背包,那我们如何确定这个较大背包被填满?你遍历到不同硬币它减出来的数值不同,所以这一点不用担心,你用到这枚硬币时,它向前找如果找到的那个背包是有一个更新过的值,那么说明那个背包一定是满的,这个背包在这个硬币的基础上,就转化为之前的小背包钱个数+1,得到该背包的答案,如何判断之前背包是否是更新过的值?这一点就要说到dp数组初始化了!

dp数组初始化:没错初始化在本题型中,也显得尤为重要,事实上在求装满背包最多或者最少的物品时候,都是很重要的。最多物品时,我们是将数组初始化为0,这一点不只是在递推公式向后推导时,为了避免起始数值影响结果,其实如果推导时候,某一个背包容量一直为0也说明,该容量无法被推出!!这种求最少的题,我们将其全部初始化为INT_MAX原因也是一样的,一个是不影响递推公式的推导,另一个是为了告诉推到的时候,如果用到之前的小背包时候,小背包如果需要硬币个数为INT_MAX那么说明,此时我们遍历的该硬币价值,无法填满我们的背包。这时候就该看一看其他硬币了。当然dp【0不能被初始化为INT_MAX因为题里已经告诉我们,目标值为0的时候,需要硬币个数为0。这也是我们如何推导dp【1】以及后面的关键所在(dp【0】如果是INT_MAX的话,无法向后推,因为一开始dp【1】初始化就是最大数,它的前面的小背包dp【0】也是最大数)。

遍历顺序:先遍历背包还是物品都没有问题。因为是完全背包,这里看不懂的朋友去看之前一期有专门比较完全背包和01背包的专题,但是有同学就要问了:即使是完全背包由于遍历顺序不同,不是也会有组合和排列的不同吗?知道这一点的同学确实很聪明,但是这里我们是求背包容量下最少物品个数,所以无论是怎么推考不考虑答案的顺序,都不能影响我们最后的答案!唯一需要注意的只有正向遍历背包!

dp打印:还是一笔带过,就是为了提醒我们,如果出现一些奇怪的bug,感觉代码完全可以通过,但是就是答案不对,那就考虑打印dp数组,看看是从哪个容量出了问题,再做分析。

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=1;j<=amount;j++){if(j>=coins[i]&&dp[j-coins[i]]!=INT_MAX)dp[j]=min(dp[j],dp[j-coins[i]]+1);}}if(dp[amount]==INT_MAX)return -1;return dp[amount];}
};

279. 完全平方数 - 力扣(LeetCode)icon-default.png?t=MBR7https://leetcode.cn/problems/perfect-squares/这一道题的思路和上一道题的思路完全相同,相信大家如果能对我上一道题的细致讲解完全吃透的话,应该不算难题。同样也是求装满背包用的最少完全平方数的数量。

dp数组的含义,递推公式,dp数组初始化,完全一样,遍历顺序也是一样的,但是遍历的for循环需要注意一下,我们求得是完全平方数,第一层循环控制数一点点自增,而不是直接给它变到平方数,因为这样有利于代码书写,当我们写第二层循环时候,再调整数据的平方。还有就是递推公式的思路是不变的,但是dp【j-coins【i】】变成了dp【j-i*i】,也就是减去这个平方数来查前面的背包是否有正确值,具体代码如下。

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++){dp[j]=min(dp[j],dp[j-i*i]+1);}}return dp[n];}
};

看到这里了,点个关注呗!如果大家有其他的不懂的题,可以翻看我之前的文章,关于一些常见算法,回溯,贪心,动态规划,往期文章都有讲解。


http://www.ppmy.cn/news/20448.html

相关文章

C++ —— 容器适配器和仿函数

目录 1.什么是容器适配器 2.stack的模拟实现 3.queue的模拟实现 4.deque概述 5.priority_queue的模拟实现 5.1仿函数 5.2模拟实现 6.反向迭代器 1.什么是容器适配器 在已有的容器(vector、list)的基础上适配出其他的容器。就类似于手机、笔记本电脑的电源适配器&…

vue前端框架课程笔记(二)

目录计算属性问题引入插值语法实现methods配置属性实现computed配置属性一些问题getter与setter注意监视属性问题引入computed和methods实现watch属性watch属性简介computed与watch的比较注意本博客参考尚硅谷官方课程&#xff0c;详细请参考 【尚硅谷bilibili官方】 本博客以…

通过 eShopOnContainers 项目学习一下微服务

这里是项目地址 https://github.com/dotnet-architecture/eShopOnContainers, 这是微软创建的一个基于 .NET 平台的微服务架构的示例应用程序&#xff0c;里面基本上市面上主流的时髦的技术都用上了。 因为涉及的内容比较多&#xff0c;所以我们只简单查看一下微服务的代码实现…

Superset权限管理

文章目录1.Superset角色1&#xff09;Admin2&#xff09;Alpha3&#xff09;Gamma4&#xff09;Sql_lab5&#xff09;Public2.实操自定义授权1&#xff09;权限集2&#xff09;实操1.Superset角色 Superset的默认角色有&#xff1a;Admin、Alpha、Gamma、sql_lab、Public 1&a…

2022个人年度总结:拒绝无效努力,实现破圈成长。

在从毕业一直到现在&#xff0c;我都会写一篇关于自己的从技术、商业、人情世故以及未来展望的博文&#xff0c;以至于归纳每个时期的自己&#xff0c; 走在互联网开发的边缘&#xff0c;不得不抽出时间鞭策自己学习新知识&#xff0c;未知的知识是 充满好奇的&#xff0c; 就好…

「计算机组成原理」计算机系统概述

文章目录一、计算机发展历程1.1 什么是计算机系统1.2 硬件的发展1.2.1 硬件发展1.2.2 摩尔定律1.3 软件的发展1.4 目前的发展趋势二、计算机系统的多级层次结构2.1 编程语言的三个等级2.2 计算机系统层次结构三、计算机硬件的基本组成3.1 冯诺依曼结构3.2 现代计算机结构四、计…

Canal快速入门

Canal 一、Canal 入门 1.1、什么是 Canal ​ 阿里巴巴 B2B 公司&#xff0c;因为业务的特性&#xff0c;卖家主要集中在国内&#xff0c;买家主要集中在国外&#xff0c;所以衍生出了同步杭州和美国异地机房的需求&#xff0c;从 2010 年开始&#xff0c;阿里系公司开始逐步…

Java基础之异常处理

一、小试牛刀 num1 / num2 当除数为零时&#xff0c;程序就会抛出异常&#xff0c;程序就会崩溃而导致退出。 我们可以通过异常处理机制来解决该问题 如果我们认为一段代码可能发生异常&#xff0c;可以使用try-catch-finally异常处理机制来解决。从而保证程序的健壮性。 将可能…