牛客题解 | 兑换零钱(一)

news/2025/2/22 15:02:44/文章来源:https://www.cnblogs.com/wc529065/p/18727237

题目

题目链接

题目主要信息:
  • 给定数组arr,arr中所有的值都为正整数且不重复
  • arr中每个值代表一种面值的货币,每种面值的货币可以使用任意
  • 组成aim的最少货币数
  • 如果无解,请返回-1
举一反三:

本题属于背包问题的一种简化版本,学习完本题的思路帮助你解决相似的背包问题。

方法一:动态规划(推荐使用)

知识点:动态规划

动态规划算法的基本思想是:将待求解的问题分解成若干个相互联系的子问题,先求解子问题,然后从这些子问题的解得到原问题的解;对于重复出现的子问题,只在第一次遇到的时候对它进行求解,并把答案保存起来,让以后再次遇到时直接引用答案,不必重新求解。动态规划算法将问题的解决方案视为一系列决策的结果。

思路:

这类涉及状态转移的题目,可以考虑动态规划。

具体做法:

  • step 1:可以用\(dp[i]\)表示要凑出i元钱需要最小的货币数。
  • step 2:一开始都设置为最大值\(aim+1\),因此货币最小1元,即货币数不会超过\(aim\).
  • step 3:初始化\(dp[0]=0\)
  • step 4:后续遍历1元到aim元,枚举每种面值的货币都可能组成的情况,取每次的最小值即可,转移方程为$ dp[i] = min(dp[i], dp[i - arr[j]] + 1)$.
  • step 5:最后比较\(dp[aim]\)的值是否超过aim,如果超过说明无解,否则返回即可。

Java实现代码:

import java.util.*;
public class Solution {public int minMoney (int[] arr, int aim) {//小于1的都返回0if(aim < 1) return 0;int[] dp = new int[aim + 1];//dp[i]表示凑齐i元最少需要多少货币数Arrays.fill(dp, aim + 1); dp[0] = 0; //遍历1-aim元for(int i = 1; i <= aim; i++){ //每种面值的货币都要枚举for(int j = 0; j < arr.length; j++){ //如果面值不超过要凑的钱才能用if(arr[j] <= i) //维护最小值dp[i] = Math.min(dp[i], dp[i - arr[j]] + 1); }}//如果最终答案大于aim代表无解return dp[aim] > aim ? -1 : dp[aim]; }
}

C++实现代码:

class Solution {
public:int minMoney(vector<int>& arr, int aim) {//小于1的都返回0if(aim < 1) return 0;//dp[i]表示凑齐i元最少需要多少货币数vector<int> dp(aim + 1, aim + 1);  dp[0] = 0; //遍历1-aim元for(int i = 1; i <= aim; i++){ //每种面值的货币都要枚举for(int j = 0; j < arr.size(); j++){ //如果面值不超过要凑的钱才能用if(arr[j] <= i) //维护最小值dp[i] = min(dp[i], dp[i - arr[j]] + 1); }}//如果最终答案大于aim代表无解return dp[aim] > aim ? -1 : dp[aim]; }
};

Python代码实现:

class Solution:def minMoney(self , arr: List[int], aim: int) -> int:#小于1的都返回0if aim < 1: return 0#dp[i]表示凑齐i元最少需要多少货币数dp = [(aim + 1) for i in range(aim + 1)] dp[0] = 0#遍历1-aim元for i in range(1, aim + 1): #每种面值的货币都要枚举for j in range(len(arr)): #如果面值不超过要凑的钱才能用if arr[j] <= i: #维护最小值dp[i] = min(dp[i], dp[i - arr[j]] + 1) #如果最终答案大于aim代表无解if dp[aim] > aim: return -1else:return dp[aim] 

复杂度分析:

  • 时间复杂度:\(O(n\cdot aim)\),第一层遍历枚举1元到aim元,第二层遍历枚举n种货币面值
  • 空间复杂度:\(O(aim)\),辅助数组dp的大小
方法二:空间记忆递归(扩展思路)

知识点:递归

递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能讲原本的问题分解为更小的子问题,这是使用递归的关键。

思路:

对于需要凑成\(aim\)的钱,第一次我们可以选择使用\(arr[0]\),则后续需要凑出\(aim-arr[0]\)的钱,那后续就是上一个的子问题,可以用递归进行。因为每种面值使用不受限,因此第一次我们可以使用arr数组中每一个,同理后续每次也可以使用arr数组中每一次,因此每次递归都要遍历arr数组,相当于分枝为\(arr.size()\)的树型递归。

具体做法:

  • step 1:递归的时候,一旦剩余需要凑出的钱为0,则找到一种情况,记录下整个的使用货币的数量,维护最小值即可。
  • step 2:一旦剩余需要凑出的钱为负,则意味着这一分枝无解,返回-1.
  • step 3:后续每次也可以使用arr数组中一次,进入子问题。
  • step 4:但是树型递归的复杂度需要\(O(aim^{n})\),重复计算过于多了,如图所示,因此我们可以用一个dp数组记录每次递归上来的结果,避免小分支重复计算,如果dp数组有值直接获取即可,不用再重复计算了。

图示:

alt

Java实现代码:

import java.util.*;
public class Solution {public int recursion(int[] arr, int aim, int[] dp){//组合超过了,返回-1if(aim < 0) return -1;//组合刚好等于需要的零钱if(aim == 0) return 0;//剩余零钱是否已经被运算过了if(dp[aim - 1] != 0) return dp[aim - 1];int Min = Integer.MAX_VALUE;//遍历所有面值for(int i = 0; i < arr.length; i++){ //递归运算选择当前的面值int res = recursion(arr, aim - arr[i], dp); //获取最小值if(res >= 0 && res < Min) Min = res + 1;}//更新最小值dp[aim - 1] = Min == Integer.MAX_VALUE ? -1 : Min; return dp[aim - 1];}public int minMoney (int[] arr, int aim) {//小于1的都返回0if(aim < 1) return 0;//dp[i]表示凑齐i元最少需要多少货币数int[] dp = new int[aim + 1];  return recursion(arr, aim, dp);}
}

C++实现代码:

class Solution {
public:int recursion(vector<int>& arr, int aim, vector<int>& dp){//组合超过了,返回-1if(aim < 0) return -1;//组合刚好等于需要的零钱if(aim == 0) return 0;//剩余零钱是否已经被运算过了if(dp[aim - 1] != 0) return dp[aim - 1];int Min = INT_MAX;//遍历所有面值for(int i = 0; i < arr.size(); i++){ //递归运算选择当前的面值int res = recursion(arr, aim - arr[i], dp); //获取最小值if(res >= 0 && res < Min) Min = res + 1;}//更新最小值dp[aim - 1] = Min == INT_MAX ? -1 : Min; return dp[aim - 1];}int minMoney(vector<int>& arr, int aim) {//小于1的都返回0if(aim < 1) return 0;//记录递归中间的值vector<int> dp(aim, 0); return recursion(arr, aim, dp);}
};

Python代码实现:

class Solution:def recursion(self, arr: List[int], aim: int, dp: List[int]) -> int:#组合超过了,返回-1if aim < 0: return -1#组合刚好等于需要的零钱if aim == 0: return 0#剩余零钱是否已经被运算过了if dp[aim - 1] != 0: return dp[aim - 1]Min = 10001#遍历所有面值for i in range(len(arr)): #递归运算选择当前的面值res = self.recursion(arr, aim - arr[i], dp) #获取最小值if res >= 0 and res < Min: Min = res + 1if Min == 10001:dp[aim - 1] = -1else:#更新最小值dp[aim - 1] = Min  return dp[aim - 1]def minMoney(self , arr: List[int], aim: int) -> int:#小于1的都返回0if aim < 1: return 0#记录递归中间的值dp = [0 for i in range(aim)] return self.recursion(arr, aim, dp)

复杂度分析:

  • 时间复杂度:\(O(n \cdot aim)\),一共需要计算aim个状态的答案,每个状态需要枚举n个面值
  • 空间复杂度:\(O(aim)\),递归栈深度及辅助数组的空间

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

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

相关文章

牛客题解 | 删除有序链表中重复的元素-I

牛客输入输出题单题解题目 题目链接 题目主要信息:给定一个从小到大排好序的链表 删去链表中重复的元素,每个值只留下一个节点举一反三: 学习完本题的思路你可以解决如下题目: BM16. 删除有序链表中重复的元素-II 方法:遍历删除(推荐使用) 思路: 既然连续相同的元素只留…

牛客题解 | 二叉树的前序遍历

牛客输入输出题单题解题目 题目链接 题目的主要信息:给定一颗二叉树的根节点,输出其前序遍历的结果举一反三: 学习完本题的思路你可以解决如下题目: BM24.二叉树的中序遍历 BM25.二叉树的后序遍历 方法一:递归(推荐使用) 知识点:二叉树递归 递归是一个过程或函数在其定…

牛客题解 | 二叉树的后序遍历

牛客输入输出题单题解题目 题目链接 题目的主要信息:给定一颗二叉树的根节点,输出其后序遍历的结果举一反三: 学习完本题的思路你可以解决如下题目: BM23.二叉树的前序遍历 BM24.二叉树的中序遍历 方法一:递归(推荐使用) 知识点:二叉树递归 递归是一个过程或函数在其定…

FB内部使用地址寄存器案例 - 32位指针区域内间接寻址

案例内容:实现DQ32模块通道依次循环输出。利用循环左移指令,把默认值位16#1的DWORD循环左移,最后绑定QD,实现DQ32模块通道依次循环输出。限制1:不使用SCL实现,而用STL或LAD来实现。 限制2:DWORD被拆分为bit的时候会因大端模式导致的输出对应异常,需要重新梳理bit位和实际…

牛客题解 | 二分查找-I

牛客输入输出题单题解题目 题目链接 题目的主要信息:给定一个元素升序的、无重复数字的整型数组 nums 和一个目标值 target 找到目标值的下标 如果找不到返回-1举一反三: 学习完本题的思路你可以解决如下题目: BM18.二维数组中的查找 BM19.寻找峰值 BM21.旋转数组 方法:二分…

ML树构建简明教程

数据准备 Teamviewer登录实验室服务器,访问http://172.17.128.86:8501/CleanData,按照页面对应的格式要求分别从NCBI和GISAID数据库下载数据,拖拽到对应的位置,点击GO即可。Gisaid DNA Accession no.|DNA INSDC|Isolate name|Collection date|SegmentNCBI Format:>{acc…

windows11安装

准备工作 1.一个8G以上的U盘 2.可以上网的电脑 3.要安装的电脑接好可上网的网线开始:把U盘的资料先备份,因为制作U盘安装系统需要格式化U盘 打开微软官网下载 https://www.microsoft.com/zh-cn/software-download/windows11选择创建 Windows 11 安装媒体,会下载一个"me…

DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?

2025年伊始,Meta创始人扎克伯格的一则声明引发全球程序员热议:“AI将在今年达到中级工程师水平,逐步接管编程工作。”与此同时,国产AI大模型DeepSeek的爆火,让一名8岁女孩仅用45分钟开发出聊天机器人的案例刷屏全网。AI的代码能力已从“辅助工具”跃升为“协同开发者”,程…

java-调用火山引擎官方API

java-调用火山引擎官方API(字节跳动旗下的AI服务平台),AI模型包括:DeepSeek、豆包、其他。。。 2025-02-20 16:33:06 星期四一、官方平台入口: 入口如下:https://console.volcengine.com/user/basics/ 1>.使用方式:1.在开放平台注册账号具体按照指引及进行注册即可,包…

reactnative 手写签名报错

有没有大佬知道这种问题怎么解决!!!!! 在线等!!!

Ollama模型迁移

为了方便本地大模型部署和迁移,本文提供了一个关于Ollama的模型本地迁移的方法。由于直接从Ollama Hub下载下来的模型,或者是比较大的GGUF模型文件,往往会被切分成多个,而文件名在Ollama的路径中又被执行了sha256散列变换。因此我们需要从索引文件中获取相应的文件名,再进…

阻塞IO 非阻塞IO

网络IO流程三次握手流程阻塞IO非阻塞IO阻塞IO和非阻塞IO的区别 阻塞IO:io 未就绪的情况下,会阻塞线程等待 非阻塞IO:io 未就绪的情况下,立即返回socket默认的情况是阻塞的