【LeetCode动态规划#09】完全背包问题实战,其二(零钱兑换和完全平方数--求物品放入个数)
零钱兑换
给定不同面额的硬币 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
示例 4:
- 输入:coins = [1], amount = 1
- 输出:1
示例 5:
- 输入:coins = [1], amount = 2
- 输出:2
提示:
- 1 <= coins.length <= 12
- 1 <= coins[i] <= 2^31 - 1
- 0 <= amount <= 10^4
思路
硬币可以无限次使用,典型的完全背包问题(正序遍历)
注意这次是要求放入硬币的个数,最少个数
不多说直接看
五步走
1、确定dp数组的含义
dp[j]:能够凑出总金额j的最少硬币数
2、确定递推公式
如何推导出dp[j]? 这里还是可以分为放入硬币和不放入硬币两种情况(详见)
(1)不放入
如果不放入硬币,那么就还是取上一层遍历递推的结果,即dp[j] (体现用一维数组解决背包问题时,数组的“滚动”,可以联系 零钱兑换II 的解释)
即dp[j] = dp[j];
(2)放入硬币
假设我们现在遍历到j - coins[i]的容量,此时能凑出总金额j - coins[i]的最少硬币数是dp[j - coins[i]]
我们要求的是总金额为j(即容量为j)的最少硬币数,显然只需j - coins[i] + coins[i]即可,也就是再放一个硬币进背包。
那么相当于此时遍历到了背包容量j,又因为硬币放入背包时要占空间的,所以背包容量还是可以表示为j - coins[i]
此时,能凑出总金额j的最少硬币数是dp[j - coins[i]] + 1,加的1是指放入的这个硬币
即dp[j] = dp[j - coins[i]] + 1;
由于我们求的是最少硬币数,因此需要取这两者中更小的那个,那么递推公式就是
dp[j] = min(dp[j], dp[j - coins[i]] + 1);
3、初始化dp数组
这里不要惯性思维直接把dp[0]初始化为1,先看看题目
题目给的示例中明确了,dp[0] = 0
那其他位置呢?因为我们要找的的是最少硬币数,也就是递推过程中的最小值
为了不影响最小值的更新,应该将其余部分的值初始化为最大整数INT_MAX
4、确定遍历顺序
本题求钱币最小个数,那么钱币有顺序和没有顺序都可以,都不影响钱币的最小个数。
所以本题并不强调集合是组合还是排列。
代码
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
//定义dp数组并初始化
//因为要找的是更小的递推值,所以初始值应该设为最大值
vector<int> dp(amount + 1, INT_MAX);
//由题目给的示例可知,dp[0]时应该是0
dp[0] = 0;
//遍历dp数组,顺序无所谓,因为要找的是使用硬币的最少个数
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 - coins[i]]是初始值则跳过
dp[j] = min(dp[j], dp[j - coins[i]] + 1);//注意这里找的是硬币个数
}
}
}
//如果递推公式推导最后发现最后的值还是初始值,说明没有找到组合,返回-1
if (dp[amount] == INT_MAX) return -1;
return dp[amount];
}
};
完全平方数
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
示例 1:
- 输入:n = 12
- 输出:3
- 解释:12 = 4 + 4 + 4
示例 2:
- 输入:n = 13
- 输出:2
- 解释:13 = 4 + 9
提示:
- 1 <= n <= 10^4
思路
"若干个完全平方数"---物品
"给定正整数 n"---背包容量
又由示例1可知,物品可以重复使用,因此这是一个完全背包
五步走
1、确定dp数组含义
dp[j]:给定整数j时,能放入使和为j的完全平方数的最少数量是dp[j]
2、确定递推公式
如何推导出dp[j]?
当然还是从两个方向:加上当前的完全平方数或者不加
所以dp[j] = min(dp[j], dp[j - i * i] + 1);这里求的是数量,所以仍然是加1
注意,所谓的"完全平方数"就是整数的平方,每个数的平方都是"完全平方数"
本题是要用"完全平方数"来装背包,所以好获取"完全平方数",只需在背包容量上限内不断循环一个自增的值,然后取平方即可(i*i)
3、初始化dp数组
联系dp定义,dp[0]:给定整数0时,能放入使和为j的完全平方数的最少数量是dp[0]
感觉dp[0]时应该是没有能放进去的(这里理由还没想好,以后可能有补充),并且题目是从1开始给的整数,就没有0这种情况
那么暂定dp[0] = 0(不行再改1),然后其余部分分的初始化还是INT_MAX(原因见上一题)
4、确定遍历顺序
完全背包,正序遍历
然后因为不是求排列或组合,遍历物品和容量时的顺序可以调换
代码
class Solution {
public:
int numSquares(int n) {
//定义dp数组
vector<int> dp(n + 1, INT_MAX);
//初始化
dp[0] = 0;
//遍历dp数组
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];
}
};
在代码实现中要注意两层for循环的处理细节
这里我们用来放入的“物品”是"完全平方数",也就是i*i
不像之前的题,我们从一个数组之类的地方把物品取出来放入就行
这里的物品需要使用循环值来“现用现算”
对于i*i <= n,其实就相当于之前的j = coins[i]; j <= amount
第二层for循环也是同理,将i*i这个整体看成物品即可
【LeetCode动态规划#09】完全背包问题实战,其二(零钱兑换和完全平方数--求物品放入个数)的更多相关文章
- 【LeetCode动态规划#05】背包问题的理论分析(基于代码随想录的个人理解,多图)
背包问题 问题描述 背包问题是一系列问题的统称,具体包括:01背包.完全背包.多重背包.分组背包等(仅需掌握前两种,后面的为竞赛级题目) 下面来研究01背包 实际上即使是最经典的01背包,也不会直接出 ...
- 【LeetCode动态规划#08】完全背包问题实战与分析(零钱兑换II)
零钱兑换II 力扣题目链接(opens new window) 给定不同面额的硬币和一个总金额.写出函数来计算可以凑成总金额的硬币组合数.假设每一种面额的硬币有无限个. 示例 1: 输入: amoun ...
- 【LeetCode动态规划#07】01背包问题一维写法(状态压缩)实战,其二(目标和、零一和)
目标和(放满背包的方法有几种) 力扣题目链接(opens new window) 难度:中等 给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S.现在你有两个符号 + 和 -.对 ...
- 【LeetCode动态规划#10】完全背包问题实战,其三(单词拆分,涉及集合处理字符串)
单词拆分 力扣题目链接(opens new window) 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词. 说明: 拆 ...
- 【LeetCode动态规划#06】分割等和子集(01背包问题一维写法实战)
分割等和子集 分割等和子集 给你一个 只包含正整数 的 非空 数组 nums .请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等. 示例 1: 输入:nums = [1,5,11,5 ...
- [LeetCode] 动态规划入门题目
最近接触了动态规划这个厉害的方法,还在慢慢地试着去了解这种思想,因此就在LeetCode上面找了几道比较简单的题目练了练手. 首先,动态规划是什么呢?很多人认为把它称作一种"算法" ...
- LeetCode:零钱兑换【322】【DP】
LeetCode:零钱兑换[322][DP] 题目描述 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成 ...
- LeetCode:322. 零钱兑换
链接:https://leetcode-cn.com/problems/coin-change/ 标签:动态规划.完全背包问题.广度优先搜索 题目 给定不同面额的硬币 coins 和一个总金额 amo ...
- leetcode动态规划题目总结
Hello everyone, I am a Chinese noob programmer. I have practiced questions on leetcode.com for 2 yea ...
- 【动态规划】简单背包问题II
问题 B: [动态规划]简单背包问题II 时间限制: 1 Sec 内存限制: 64 MB提交: 21 解决: 14[提交][状态][讨论版] 题目描述 张琪曼:“为什么背包一定要完全装满呢?尽可能 ...
随机推荐
- 【转帖】FIO磁盘性能测试工具
https://www.jianshu.com/p/70b8c7d5d217 FIO工具介绍 FIO 工具是一款用于测试硬件存储性能的辅助工具,兼具灵活性.可靠性从而从众多性能测试工具中脱颖而出.磁盘 ...
- [转帖]PowerShell教程 - 日期时间管理(Date & Time Management)
https://www.cnblogs.com/cqpanda/p/16589991.html 更新记录转载请注明出处.2022年8月25日 发布.2022年8月18日 从笔记迁移到博客. 日期时间管 ...
- CTT 总结
Day 1 T1 肝 4 个半小时,主要是前面各种假,中途各种改.出来听说 T2 才是签子.但是 T2 只写了 15 分暴力.100+15+0=115. Day 2 T1 肝 4 个小时,但是 8 点 ...
- Unity2019及Unity2020打包android的环境配置
2019安卓打包只有gradle模式了,因为谷歌把adt删了 unity2019可以自定义gradle模板,国内请把repo地址改成阿里云的源 unity2019打apk配置 引擎版本:unity20 ...
- SqlSugar的Select用法
Select 位置 正常情况:应该在最后面, 一般是 .Where(..).OrderBy(..).Select(..).ToList() 特殊情况:如果Select不是最后一个位置,则Select要 ...
- 解决问题:ACM-Reference-Format使用这个style后,文献引用顺序和正文不一致
解决问题:latex中bib引用顺序不正确,引用顺序和正文不一致_丨汀.的博客-CSDN博客问题:生成pdf时文献应用会乱序引用bib格式的参考文献时,会这么写:\bibliographystyle{ ...
- python3 ACM模式的输入输出例子教学
Python的输入是字符串,所以要自己转类型 strip去掉左右两端的空白符,返回str slipt把字符串按空白符拆开,返回[str] map把list里面的值映射到指定类型,返回[type] EO ...
- 19.4 Boost Asio 远程命令执行
命令执行机制的实现与原生套接字通信一致,仅仅只是在调用时采用了Boost通用接口,在服务端中我们通过封装实现一个run_command函数,该函数用于发送一个字符串命令,并循环等待接收客户端返回的字符 ...
- 8.1 TEB与PEB概述
在开始使用TEB/PEB获取进程或线程ID之前,我想有必要解释一下这两个名词,PEB指的是进程环境块(Process Environment Block),用于存储进程状态信息和进程所需的各种数据.每 ...
- Java21 + SpringBoot3整合springdoc-openapi,自动生成在线接口文档,支持SpringSecurity和JWT认证方式
目录 前言 相关技术简介 OpenAPI Swagger Springfox springdoc swagger2与swagger3常用注解对比 实现步骤 引入maven依赖 修改配置文件 设置api ...