LeetCode零钱兑换(动态规划)

题目描述

给你一个整数数组 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, 2, 5],目标是 120,问最少需要的硬币个数?

  • 我们要分解子问题,分层级找最优子结构,看到这又要晕了哈,憋急~~ 下面马上举例。

  • 这里我们使用「自顶向下」思想来考虑这个题目,然后用「自底向上」的方法来解题。

  • dp是遍历金额总数构造的数组,dp[i]: 表示总金额为 i 的时候最优解法的硬币数,求dp[i]可以在dp中复用子问题解。

  • 我们想一下:求总金额 120 有几种方法?下面这个思路关键了 !!!
    一共有 3 种方式,因为我们有 3 种不同面值的硬币。
    1.拿一枚面值为 1 的硬币 + 总金额为 119 的最优解法的硬币数量
    这里我们只需要假设总金额为 119 的最优解法的硬币数有人已经帮我们算好了,
    不需要纠结于此。(虽然一会也是我们自己算,哈哈)
    即:dp[119] + 1
    2.拿一枚面值为 2 的硬币 + 总金额为 118 的最优解法的硬币数
    这里我们只需要假设总金额为 118 的最优解法的硬币数有人已经帮我们算好了
    即:dp[118] + 1
    3.拿一枚面值为 5 的硬币 + 总金额为 115 的最优解法的硬币数
    这里我们只需要假设总金额为 115 的最优解法的硬币数有人已经帮我们算好了
    即:dp[115] + 1
    因为硬币的金额已知,所以dp[120]只能由这三种方法计算得到

    • 所以,总金额为 120 的最优解法就是上面这三种解法中最优的一种,也就是硬币数最少
      的一种,我们下面试着用代码来表示一下:

    • dp[120] = Math.min(dp[119] + 1, dp[118] + 1, dp[115] + 1);

    • 推导出「状态转移方程」:

dp[i] = Math.min(dp[i - coin] + 1, dp[i - coin] + 1, ...)

其中 coin 有多少种可能,我们就需要比较多少次,那么我们到底需要比较多少次呢? 当然是 coins 数组中有几种不同面值的硬币,就是多少次了~ 遍历 coins 数组, 分别去对比即可

  • 上面方程中的 dp[119]dp[118]dp[115] 我们继续用这种思想去分解,
    这就是动态规划了,把这种思想,思考问题的方式理解了,这一类型的题目
    问题都不会太大。

代码

var coinChange = function(coins, amount) {let dp = new Array(amount + 1).fill(Infinity); // 初始化 dp 数组dp[0] = 0; // 凑出金额 0 所需的硬币数为 0for (let i = 1; i <= amount; i++) { // 遍历所有金额从 1 到 amountfor (let coin of coins) { // 遍历所有硬币面额if (i - coin >= 0) { // 如果当前金额 i 大于等于硬币面额 coindp[i] = Math.min(dp[i], dp[i - coin] + 1); // 更新 dp[i] 的值}}}return dp[amount] === Infinity ? -1 : dp[amount]; // 如果 dp[amount] 仍为 Infinity,说明无法凑出金额
};

代码分析

1. 初始化 dp 数组
let dp = new Array(amount + 1).fill(Infinity); // 初始化 dp 数组
dp[0] = 0; // 凑出金额 0 所需的硬币数为 0
  • dp 数组的作用dp[i] 表示凑出金额 i 所需的最少硬币数。
  • 为什么用 Infinity 初始化:因为对于大多数金额,我们一开始不知道需要多少硬币才能凑出,所以用一个很大的数(Infinity)来表示“尚未计算”。
  • dp[0] = 0:凑出金额 0 显然不需要任何硬币,所以 dp[0] 初始化为 0
2. 外层循环:遍历所有金额
for (let i = 1; i <= amount; i++) { // 遍历所有金额从 1 到 amount
  • 作用:我们需要计算从金额 1 到目标金额 amount 的每一个金额的最少硬币数。
  • 逐步计算:从最小的金额开始,逐步向上计算,直到目标金额。这样可以确保在计算 dp[i] 时,所有小于 i 的金额的最少硬币数已经计算好了。
3. 内层循环:遍历所有硬币面额
for (let coin of coins) { // 遍历所有硬币面额
  • 作用:对于每个金额 i,我们需要考虑所有可能的硬币面额,看看用哪种硬币可以更优地凑出金额 i
  • 逐个尝试:假设我们有硬币面额 [1, 2, 5],对于金额 3,我们会尝试:
    • 用一枚面值为 1 的硬币,然后看 dp[2] 的值。
    • 用一枚面值为 2 的硬币,然后看 dp[1] 的值。
    • 用一枚面值为 5 的硬币,但 3 - 5 < 0,所以不能用。
4. 状态转移:更新 dp[i]
if (i - coin >= 0) { // 如果当前金额 i 大于等于硬币面额 coindp[i] = Math.min(dp[i], dp[i - coin] + 1); // 更新 dp[i] 的值
}
  • 条件检查if (i - coin >= 0) 确保我们不会用一个比当前金额还大的硬币,否则没有意义。
  • 状态转移逻辑
    • 假设我们正在计算金额 i,并且考虑用一枚面值为 coin 的硬币。
    • 如果我们用这枚硬币,那么剩下的金额就是 i - coin,凑出这个金额所需的硬币数是 dp[i - coin]
    • 因为我们用了一枚硬币,所以总硬币数是 dp[i - coin] + 1
    • 我们需要比较所有可能的硬币面额,选择最小的硬币数。这就是 Math.min(dp[i], dp[i - coin] + 1) 的作用。
5. 返回结果
return dp[amount] === Infinity ? -1 : dp[amount]; // 如果 dp[amount] 仍为 Infinity,说明无法凑出金额
  • 检查结果:如果 dp[amount] 仍然是 Infinity,说明我们无法用给定的硬币面额凑出目标金额 amount,因此返回 -1
  • 返回最少硬币数:如果 dp[amount] 是一个有限的数,说明我们成功地找到了最少硬币数,直接返回它。

举例说明

假设 coins = [1, 2, 5]amount = 11

初始化
  • dp = [0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity]
计算过程
  1. 金额 i = 1

    • 遍历硬币面额:
      • coin = 1dp[1] = Math.min(Infinity, dp[0] + 1) = 1
    • 结果:dp = [0, 1, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity]
  2. 金额 i = 2

    • 遍历硬币面额:
      • coin = 1dp[2] = Math.min(Infinity, dp[1] + 1) = 2
      • coin = 2dp[2] = Math.min(2, dp[0] + 1) = 1
    • 结果:dp = [0, 1, 1, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity]
  3. 金额 i = 3

    • 遍历硬币面额:
      • coin = 1dp[3] = Math.min(Infinity, dp[2] + 1) = 2
      • coin = 2dp[3] = Math.min(2, dp[1] + 1) = 2
    • 结果:dp = [0, 1, 1, 2, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity]
  4. 金额 i = 4

    • 遍历硬币面额:
      • coin = 1dp[4] = Math.min(Infinity, dp[3] + 1) = 3
      • coin = 2dp[4] = Math.min(3, dp[2] + 1) = 2
    • 结果:dp = [0, 1, 1, 2, 2, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity]
  5. 金额 i = 5

    • 遍历硬币面额:
      • coin = 1dp[5] = Math.min(Infinity, dp[4] + 1) = 3
      • coin = 2dp[5] = Math.min(3, dp[3] + 1) = 3
      • coin = 5dp[5] = Math.min(3, dp[0] + 1) = 1
    • 结果:dp = [0, 1, 1, 2, 2, 1, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity]
  6. 金额 i = 6

    • 遍历硬币面额:
      • coin = 1dp[6] = Math.min(Infinity, dp[5] + 1) = 2
      • coin = 2dp[6] = Math.min(2, dp[4] + 1) = 2
      • coin = 5dp[6] = Math.min(2, dp[1] + 1) = 2
    • 结果:dp = [0, 1, 1, 2, 2, 1, 2, Infinity, Infinity, Infinity, Infinity, Infinity]
  7. 金额 i = 7

    • 遍历硬币面额:
      • coin = 1dp[7] = Math.min(Infinity, dp[6] + 1) = 3
      • coin = 2dp[7] = Math.min(3, dp[5] + 1) = 2
      • coin = 5dp[7] = Math.min(2, dp[2] + 1) = 2
    • 结果:dp = [0, 1, 1, 2, 2, 1, 2, 2, Infinity, Infinity, Infinity, Infinity]
  8. 金额 i = 8

    • 遍历硬币面额:
      • coin = 1dp[8] = Math.min(Infinity, dp[7] + 1) = 3
      • coin = 2dp[8] = Math.min(3, dp[6] + 1) = 3
      • coin = 5dp[8] = Math.min(3, dp[3] + 1) = 3
    • 结果:dp = [0, 1, 1, 2, 2, 1, 2, 2, 3, Infinity, Infinity, Infinity]
  9. 金额 i = 9

    • 遍历硬币面额:
      • coin = 1dp[9] = Math.min(Infinity, dp[8] + 1) = 4
      • coin = 2dp[9] = Math.min(4, dp[7] + 1) = 3
      • coin = 5dp[9] = Math.min(3, dp[4] + 1) = 3
    • 结果:dp = [0, 1, 1, 2, 2, 1, 2, 2, 3, 3, Infinity, Infinity]
  10. 金额 i = 10

    • 遍历硬币面额:
      • coin = 1dp[10] = Math.min(Infinity, dp[9] + 1) = 4
      • coin = 2dp[10] = Math.min(4, dp[8] + 1) = 4
      • coin = 5dp[10] = Math.min(4, dp[5] + 1) = 2
    • 结果:dp = [0, 1, 1, 2, 2, 1, 2, 2, 3, 3, 2, Infinity]
  11. 金额 i = 11

    • 遍历硬币面额:
      • coin = 1dp[11] = Math.min(Infinity, dp[10] + 1) = 3
      • coin = 2dp[11] = Math.min(3, dp[9] + 1) = 3
      • coin = 5dp[11] = Math.min(3, dp[6] + 1) = 3
    • 结果:dp = [0, 1, 1, 2, 2, 1, 2, 2, 3, 3, 2, 3]

最终结果

  • dp[11] = 3,表示凑出金额 11 所需的最少硬币数为 3

总结

这段代码通过动态规划的思想,逐步计算出每个金额的最少硬币数,最终得到目标金额的最少硬币数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/900608.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

/sys/fs/cgroup/memory/memory.stat 关键指标说明

目录 1. **total_rss**2. **total_inactive_file**3. **total_active_file**4. **shmem**5. **其他相关指标**总结 以下是/sys/fs/cgroup/memory/memory.stat文件中一些关键指标的详细介绍&#xff0c;特别是与PostgreSQL相关的指标&#xff1a; 1. total_rss 定义&#xff1…

C++第14届蓝桥杯b组学习笔记

1. 日期统计 小蓝现在有一个长度为 100100 的数组&#xff0c;数组中的每个元素的值都在 00 到 99 的范围之内。数组中的元素从左至右如下所示&#xff1a; 5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 3 0 3 7 9 2 7 0 5 8 8 5 7 0 9 9 1 9 4 4…

[Effective C++]条款28:避免返回handles指向对象内部成分

. 在C中&#xff0c;返回指向对象内部成分的引用&#xff08;handles&#xff09;可能会导致封装性降低和对象空悬问题。为了避免這些问题&#xff0c;可以通过返回const引用来限制对内部数据的修改&#xff0c;从而确保只读访问 1、返回内部引用对象 下面代码中getData函数返…

PyTorch 学习笔记

环境&#xff1a;python3.8 PyTorch2.4.1cpu PyCharm 参考链接&#xff1a; 快速入门 — PyTorch 教程 2.6.0cu124 文档 PyTorch 文档 — PyTorch 2.4 文档 快速入门 导入库 import torch from torch import nn from torch.utils.data import DataLoader from torchvision …

windows开启wsl与轻量级虚拟机管理

基于win 10 打造K8S应用开发环境&#xff08;wsl & kind&#xff09; 一、wsl子系统安装 1.1 确认windows系统版本 cmd/powershell 或者win r 运行winver 操作系统要> 19044 1.2 开启wsl功能 控制面板 -> 程序 -> 启用或关闭Windows功能 开启适用于Linux的…

C++ -异常之除以 0 问题(整数除以 0 编译时检测、整数除以 0 运行时检测、浮点数除以 0 编译时检测、浮点数除以 0 运行时检测)

一、整数除以 0&#xff08;编译时检测&#xff09; 1、演示 #include <iostream>using namespace std;int main() {int result 10 / 0;cout << result << endl;return 0; }程序无法运行&#xff0c;输出结果 error C2124: 被零除或对零求模2、演示解读 …

【蓝桥杯】搜索算法:剪枝技巧+记忆化搜索

1. 可行性剪枝应用 1.1. 题目 题目描述: 给定一个正整数n和一个正整数目标值target,以及一个由不同正整数组成的数组nums。要求从nums中选出若干个数,每个数可以被选多次,使得这些数的和恰好等于target。问有多少种不同的组合方式? 输入: 第一行:n和target,表示数组…

Uniapp 集成极光推送(JPush)完整指南

文章目录 前言一、准备工作1. 注册极光开发者账号2. 创建应用3. Uniapp项目准备 二、集成极光推送插件方法一&#xff1a;使用UniPush&#xff08;推荐&#xff09;方法二&#xff1a;手动集成极光推送SDK 三、配置原生平台参数四、核心功能实现1. 获取RegistrationID2. 设置别…

Linux中进程

一、认识进程 进程(PCB)内核数据结构(task_struct)程序的代码和数据 每一个进程都有其独立的task_struct,OS对众多的task_struct进行管理&#xff0c;如何管理&#xff1f;先描述再组织&#xff0c;所有运⾏在系统⾥的进程都以task_struct链表的形式存在内核⾥&#xff0c;而…

国外的AI工具

一 OpenAI &#xff1a; &#x1f4a1; 总览&#xff1a; 名称全称/代号简介GPT-4o“o” omniOpenAI 最新的旗舰多模态模型&#xff08;文字、图像、音频三模态&#xff09;&#xff0c;比 GPT-4 更强、更快、更便宜。GPT-4o-mini精简版 GPT-4o轻量级版本&#xff0c;推测为性…

企业级Java开发工具MyEclipse v2025.1——支持AI编码辅助

MyEclipse一次性提供了巨量的Eclipse插件库&#xff0c;无需学习任何新的开发语言和工具&#xff0c;便可在一体化的IDE下进行Java EE、Web和PhoneGap移动应用的开发&#xff1b;强大的智能代码补齐功能&#xff0c;让企业开发化繁为简。 立即获取MyEclipse v2025.1正式版 具…

按键长按代码

这些代码都存放在定时器中断中。中断为100ms中断一次。 数据判断&#xff0c;看的懂就看吧

在 macOS 上连接 PostgreSQL 数据库(pgAdmin、DBeaver)

在 macOS 上连接 PostgreSQL 数据库 pgAdmin 官方提供的图形化管理工具&#xff0c;支持 macOS。 下载地址&#xff1a;https://www.pgadmin.org/ pgAdmin 4 是对 pgAdmin 的完全重写&#xff0c;使用 Python、ReactJs 和 Javascript 构建。一个用 Electron 编写的桌面运行时…

FTP协议和win server2022安装ftp

FTP协议简介 FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09;是一种用于在网络上的计算机之间传输文件的标准网络协议。它被广泛应用于服务器与客户端之间的文件上传、下载以及管理操作。FTP支持多种文件类型和结构&#xff0c;并提供了相对简单的…

人工智能——AdaBoost算法

目录 摘要 13 AdaBoost算法 13.1 本章工作任务 13.2 本章技能目标 13.3 本章简介 13.4 编程实战 13.5 本章总结 13.6 本章作业 本章已完结! 摘要 本章实现的工作是:首先采用Python语言读取数据并构造训练集和测试集。然后建立AdaBoost模型,利用训练集训练该模型,…

DFS 蓝桥杯

最大数字 问题描述 给定一个正整数 NN 。你可以对 NN 的任意一位数字执行任意次以下 2 种操 作&#xff1a; 将该位数字加 1 。如果该位数字已经是 9 , 加 1 之后变成 0 。 将该位数字减 1 。如果该位数字已经是 0 , 减 1 之后变成 9 。 你现在总共可以执行 1 号操作不超过 A…

【开发经验】调试OpenBMC Redfish EventService功能

EventService功能是Redfish规范中定义的一种事件日志的发送方式。用户可以设置订阅者信息(通常是一个web服务器)&#xff0c;当产生事件日志时&#xff0c;OpenBMC可以根据用户设置的订阅者信息与对日志的筛选设置&#xff0c;将事件日志发送到订阅者。 相比于传统的SNMPTrap日…

中断嵌套、中断咬尾、中断晚到

中断咬尾&#xff08;Tail-Chaining&#xff09;是一种通过减少上下文切换开销来实现中断连续响应的高效机制&#xff0c;其核心在于避免重复的出栈和入栈操作&#xff0c;从而显著降低中断延迟。以下是具体原理及实现方式&#xff1a; 中断咬尾的运作机制 当多个中断请求连续…

Vue2下载二进制文件

后端&#xff1a; controller: GetMapping(value "/get-import-template")public void problemTemplate(HttpServletRequest request, HttpServletResponse response) throws Exception {iUserService.problemTemplate(request, response);} service: void probl…

Ubuntu小练习

文章目录 一、远程连接1、通过putty连接2、查看putty运行状态3、通过Puuty远程登录Ubuntu4、添加新用户查看是否添加成功 5、用新用户登录远程Ubuntu6、使用VNC远程登录树莓派 二、虚拟机上talk聊天三、Opencv1、简单安装版&#xff08;适合新手安装&#xff09;2、打开VScode特…