518. 零钱兑换 II(力扣LeetCode)
文章目录
- 518. 零钱兑换 II
- 题目描述
- 动态规划
- 一维数组
- 为什么不能交换两个for循环的顺序?
- 二维数组
518. 零钱兑换 II
题目描述
给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。
假设每一种面额的硬币有无限个。
题目数据保证结果符合 32 位带符号整数。
示例 1:
输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
示例 2:
输入:amount = 3, coins = [2]
输出:0
解释:只用面额 2 的硬币不能凑成总金额 3 。
示例 3:
输入:amount = 10, coins = [10]
输出:1
提示:
- 1 <= coins.length <= 300
- 1 <= coins[i] <= 5000
- coins 中的所有值 互不相同
- 0 <= amount <= 5000
动态规划
一维数组
这段代码是在解决一个称为"零钱兑换 II"的动态规划问题。具体来说,它计算了使用一个无限数量的不同面额硬币凑成某个总金额的所有可能组合的数目。代码使用了C++语言。下面是对这段代码的逐行解释:
// 定义一个Solution类
class Solution {
public:// 定义并实现一个名为change的公共成员函数int change(int amount, vector<int>& coins) {// dp定义了一个整型向量(dp数组),大小为amount + 1,所有值初始化为0vector<int>dp(amount+1, 0);// 将dp数组的第一项设置为1,这表示金额为0的话有1种方式(就是不用任何硬币)dp[0] = 1;// 遍历coins数组,i为索引,从0开始到coins.size() - 1for(int i = 0; i < coins.size(); i++)// 从当前硬币面额coins[i]开始,直到总金额for(int j = coins[i]; j <= amount; j++)// 更新dp[j]的值:dp[j] = dp[j] + dp[j - coins[i]]// 这表示加上用当前的硬币面额coins[i]凑出金额j的新组合数dp[j] += dp[j - coins[i]];// 最后返回dp数组的最后一项,即凑成总金额amount的组合数return dp[amount];}
};
解释一下这个动态规划的核心思想:
- dp数组的定义:dp[i]表示凑成总金额为i的硬币组合数。
- 初始化:由于凑成总金额为0的方式只有一种,就是不使用任何硬币,所以初始化dp[0] = 1。
- 外层循环:遍历每一种硬币。
- 内层循环:对于每一种硬币,更新金额从硬币面额到总金额的所有dp值。
- 状态转移方程:dp[j] += dp[j - coins[i]]的含义是,对于每个金额j,考虑当前的硬币面额coins[i],如果我们知道了组成金额j - coins[i]的组合数(即dp[j - coins[i]]),那么就可以通过添加一个coins[i]硬币来得到金额j的一个新的组合。所以,我们要将组成金额j - coins[i]的所有组合数加到dp[j]上。
这个算法的时间复杂度是O(n*m),n是金额amount,m是硬币种类数。空间复杂度是O(n),因为我们使用了一个n + 1大小的dp数组。
为什么不能交换两个for循环的顺序?
在这个特定的动态规划问题中,两个for
循环的顺序决定了硬币组合数是如何被计算的,特别是它们影响了状态转移方程的更新方式。如果交换了循环的顺序,将会改变动态规划算法的整个逻辑。
原始顺序(先遍历硬币,然后遍历金额)的循环确保了以下几点:
-
组合性:当计算
dp[j]
时(即凑成金额j
的方法数),算法仅考虑使用当前硬币和之前考虑过的硬币的组合。这意味着,对于每个硬币,你都是在累积之前的结果上增加新的可能性。 -
无重复:这个顺序确保了在计算组合数时,相同面额的硬币不会被重复计算。即我们不会得到多个考虑顺序不同但实际上相同的硬币组合。
-
有序:由于每次循环都是在之前硬币的基础上加上新硬币,因此所有的组合都是以一种有序的方式生成的。
如果交换了for
循环的顺序,算法将会改变上述逻辑,导致以下问题:
- 每次计算
dp[j]
时,都会考虑所有硬币,这意味着同一金额会被重复计算多次,从而破坏了动态规划的基本原则。 - 容易出现重复组合,因为对于每个金额,你都在尝试所有的硬币,无法保证硬币使用的唯一顺序,从而可能导致重复的组合方式被计数。
- 缺乏顺序性,因为你在每个金额的计算中都包含了所有的硬币,这样就会有多种硬币组合顺序产生相同的金额,这些都被错误地算作不同的组合。
因此,保持原始的循环顺序对于解决这个动态规划问题是很重要的,它确保了算法的正确性和效率。
二维数组
这段代码是解决"零钱兑换 II"问题的动态规划解法。下面是对代码的详细注释:
class Solution {
public:int change(int amount, vector<int>& coins) {// 初始化动态规划表格。行数为coins.size()+1,代表0到coins.size个硬币,列数为amount+1,代表0到amount的金额。// dp[i][j]表示使用前i种硬币凑成金额j的方法数。vector<vector<int>> dp(coins.size()+1,vector<int>(amount+1,0));// base case:当金额为0时,即j=0,不使用任何硬币就能凑齐,因此方法数为1。dp[0][0]=1;// 遍历所有的硬币种类for(int i=1;i<=coins.size();i++){// 对于每一种硬币,遍历所有可能的金额for(int j=0;j<=amount;j++){// dp[i][j]首先继承dp[i-1][j]的值,即不使用第i种硬币时凑成金额j的方法数。dp[i][j]=dp[i-1][j];// 如果当前金额j大于等于当前考虑的硬币面额coins[i-1],// 则可以考虑使用这种硬币。此时的方法数为dp[i][j-coins[i-1]],// 加上这种硬币之前的方法数,因为dp[i][j-coins[i-1]]已经计算了使用前i种硬币凑成金额j-coins[i-1]的方法数。if(j>=coins[i-1])dp[i][j]+=dp[i][j-coins[i-1]];}}// 返回使用所有硬币凑成总金额amount的方法数。return dp[coins.size()][amount];}
};
核心思想:
- 动态规划表
dp
的大小为(coins.size()+1) * (amount+1)
,dp[i][j]
表示使用前i
种硬币(其中硬币种类从0开始编号)凑成金额j
的方法数。 - 初始化
dp[0][0]=1
表示不使用任何硬币凑成金额0的方法有1种。 - 外层循环遍历硬币种类,内层循环遍历可能的金额。
- 对于每一种硬币
coins[i-1]
和每一个金额j
,如果j
大于等于当前硬币的面额,我们有两个选择:不使用当前的硬币(继承dp[i-1][j]
的值),或者使用当前的硬币(dp[i][j-coins[i-1]]
加上当前硬币的数量)。 - 最终,
dp[coins.size()][amount]
是使用所有硬币凑成金额amount
的方法数。
相关文章:
518. 零钱兑换 II(力扣LeetCode)
文章目录 518. 零钱兑换 II题目描述动态规划一维数组为什么不能交换两个for循环的顺序? 二维数组 518. 零钱兑换 II 题目描述 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数…...
01串的熵(蓝桥杯)
文章目录 01串的熵问题描述答案:11027421题意解释暴力枚举 01串的熵 问题描述 对于一个长度为n的01串 S x 1 x 2 x 3 x_{1}x_{2}x_{3} x1x2x3… x n x_{n} xn,香农信息熵的定义为 H(S) − ∑ 1 n p ( x i ) l o g 2 ( p ( x i ) ) -\sum _{1…...

Rust 基础语法和数据类型
数据类型 Rust提供了一系列的基本数据类型,包括整型(如i32、u32)、浮点型(如f32、f64)、布尔类型(bool)和字符类型(char)。此外,Rust还提供了原生数组、元组…...
【Java SE】10 String类
目录 1. String类的重要性 2.常用方法 2.1字符串构造 2.2 String对象的比较 2.3字符串查找 2.4转化 2.5字符串替换 2.6字符串拆分 2.7字符串截取 2.8其他操作方法 2.9字符串的不可变性 2.10字符串修改 3. StringBuffer和StringBuilder 3.1StringBuilder的介绍 4.…...
web蓝桥杯真题:新鲜的蔬菜
代码: .box {display: flex; } #box1 {align-items: center;justify-content: center; }#box2 {justify-content: space-between; } #box2 .item:nth-child(2) {align-self: end; }#box3 {justify-content: space-between; } #box3 .item:nth-child(2) {align-self…...

超声波清洗机能洗哪些东西?洗眼镜超声波清洗机推荐
在现代生活中,人们对清洁卫生的要求越来越高,尤其是对一些细小物件的清洁。眼镜作为我们日常生活中不可或缺的物品,清洁保养更是至关重要。传统的清洗方式可能无法完全清洁眼镜表面的细菌和污垢,于是超声波清洗机成为了很多人的选…...
[C++][算法基础]走迷宫(BFS)
给定一个 nm 的二维整数数组,用来表示一个迷宫,数组中只包含 0 或 1,其中 0 表示可以走的路,1 表示不可通过的墙壁。 最初,有一个人位于左上角 (1,1)(1,1) 处,已知该人每次可以向上、下、左、右任意一个方…...

C语言字符串左旋
一、前言 这个题目的完整题目是这样子的。 二、我们实现这个编程的思路 2.1暴力破解思想 假如有一个数组里面的字符串为”abcdef“,我们这时候就这样先将字符”a“移到最后再将其余的字符前移。 2.2三步移动法 同样我们还是假设一个数组里面存的是字符串”abcd…...
Linux 中断会产生嵌套吗?
文章目录 1. 前言2. Linux 中断是否会嵌套?2.1 分析背景2.2 中断处理抢占、嵌套可能性分析2.3 中断处理抢占、嵌套小结 3. 参考资料 1. 前言 限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。 2. …...

嵌入式ARM版本银河麒麟操作系统V10SP1安装OPenGauss数据库
前言: 官网提供了非常完整的openGauss安装步骤。 https://opengauss.org/zh/download/archive/列举一下个人的使用环境: 麒麟V10 rk3588工控板(ARM) openGauss-3.0.5(极简版)浏览一下官网,可以…...
深度学习八股文
Bert旨在通过联合左侧和右侧的上下文,从未标记文本中预训练出一个深度双向表示模型。因此,BERT可以通过增加一个额外的输出层来进行微调,就可以达到为广泛的任务创建State-of-the-arts 模型的效果,比如QA、语言推理任务。Bert的构…...
jquery 自整理
echarts官方:Documentation - Apache ECharts 1、CheckBox复选框 //选中事件(页面点击) $(#operateExit).on(ifChecked, function(){ $(input[name"operateExit"]).val(1); }); //非选中事件ÿ…...
MySQL | 加索引报错
报错信息 1170 - BLOB/TEXT column user_name used in key specification without a key length解决方案 分析 这个错误通常是因为尝试在一个包含BLOB或TEXT类型列的列上创建索引时没有指定键的长度。MySQL要求在使用BLOB或TEXT类型列作为索引键时,必须指定键的长…...

前端:自制年历
详细思路可以看我的另一篇文章《前端:自制月历》,基本思路一致,只是元素布局略有差异 ①获取起始位startnew Date(moment().format(yyyy-01-01)).getDay() ②获取总的格子数numMath.ceil(365/7)*7,这里用365或者366计算结果都是一样的371 …...
9.手写JavaScript大数相加问题
一、核心思想 找到两个字符串中最长的长度,对两个字符串在头位置补0达到相等的长度,相加时注意进位和类型转换,特别考虑当相加到第一位是如果仍然有进位不要忽略。此外,js中允许使用的最大的数字为 console.log("最大数&qu…...

FPGA开源项目分享——基于 DE1-SOC 的 String Art 实现
导语 今天继续康奈尔大学FPGA课程ECE 5760的典型案例分享——基于DE1-SOC的String Art实现。 (更多其他案例请参考网站: Final Projects ECE 5760) 1. 项目概述 项目网址 ECE 5760 Final Project 项目说明 String Art起源于19世纪的数学…...
通过 CLI 和引入的方式使用 React:基础入门
使用React 有两种使用方式,主要有以下几个原因: 灵活性和适应性: 引入的方式可以让开发者在现有的 HTML 页面中快速引入 React,无需设置完整的项目环境。这适合小型或原型项目。 CLI 方式则更适合用于构建大型复杂的 React 应用程序,因为它提供了更完整的项目结构和…...

第三资本:铸就辉煌非凡的资历
第三资本香港有限公司在在金融投资领域一直以专业精神和不懈追求获得良好名声,近几年在国际资本市场上更是写下了辉煌的章节。针对第三资本而言,专业是基本,也是成功的唯一途径。投资总监刘国海解释道:“金融从业者务必深入把握专业能力,对行业现状敏感,重视风险管控,才能在这个…...

基于激光雷达的袋装水泥智能装车系统有哪些优势?
激光雷达技术在水泥机械智能化中发挥着举足轻重的作用,特别在袋装水泥智能装车系统的应用中表现得尤为突出。 由因泰立科技精心打造的基于激光雷达的袋装水泥智能装车系统,不仅大幅缩短了装车码垛的时间,降低了工人的劳动强度,还显…...
实战自动化修改主机名
一、主程序 #!/bin/bash# 设置主机名为node01 set_hostname() {local new_hostname$1echo "正在设置主机名为 $new_hostname ..."# 使用hostnamectl设置主机名hostnamectl set-hostname $new_hostname# 检查主机名是否更改成功if [ "$(hostname)" "…...

测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...

从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...