Golang每日一练(leetDay0110) 零钱兑换I\II Coin Change

文章介绍了两个与零钱兑换相关的问题,CoinChange和CoinChangeII,分别给出了解决这两个问题的动态规划算法。对于CoinChange,目标是找到凑成给定金额的最少硬币数量,而CoinChangeII关注的是组合数。文章提供了详细的代码实现和示例解释。

 

目录

322. 零钱兑换 Coin Change  🌟🌟

518. 零钱兑换 II Coin Change ii  🌟🌟

🌟 每日一练刷题专栏 🌟

Rust每日一练 专栏

Golang每日一练 专栏

Python每日一练 专栏

C/C++每日一练 专栏

Java每日一练 专栏


322. 零钱兑换 Coin Change

给你一个整数数组 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 <= coins.length <= 12
  • 1 <= coins[i] <= 2^31 - 1
  • 0 <= amount <= 10^4

代码: 动态规划

状态方程: dp[0] = 0, dp[i] = min(dp[i], dp[i-coin]+1)

package main

import (
	"fmt"
	"math"
)

func coinChange(coins []int, amount int) int {
	dp := make([]int, amount+1)
	for i := 1; i <= amount; i++ {
		dp[i] = math.MaxInt32
		for _, coin := range coins {
			if i >= coin {
				dp[i] = min(dp[i], dp[i-coin]+1)
			}
		}
	}
	if dp[amount] == math.MaxInt32 {
		return -1
	}
	return dp[amount]
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func main() {
	coins := []int{1, 2, 5}
	amout := 11
	fmt.Println(coinChange(coins, amout))
	coins = []int{2}
	amout = 3
	fmt.Println(coinChange(coins, amout))
	coins = []int{1}
	amout = 0
	fmt.Println(coinChange(coins, amout))
}

输出:

3
-1
0

注:创建一个长度为 amount+1 的一维数组 dp,并将 dp 中的所有元素初始化为一个非常大的数,这里我们使用了 math.MaxInt32。在循环遍历 dp 的过程中,对于每个 i,都遍历一遍硬币数组 coins,分别判断是否能使用每个硬币来凑出 i 元,如果可以,则使用当前的硬币更新 dp[i]。最后,如果 dp[amount] 仍然为一个非常大的数,说明无法凑出 amount 元,返回 -1,否则返回 dp[amount]。


518. 零钱兑换 II Coin Change 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

代码: 动态规划

状态方程: dp[0] = 1, dp[i] += dp[i-coin]

package main

import "fmt"

func change(coins []int, amount int) int {
	dp := make([]int, amount+1)
	dp[0] = 1
	for _, coin := range coins {
		for i := coin; i <= amount; i++ {
			dp[i] += dp[i-coin]
		}
	}
	return dp[amount]
}

func main() {
	coins := []int{1, 2, 5}
	amout := 5
	fmt.Println(change(coins, amout))
	coins = []int{2}
	amout = 3
	fmt.Println(change(coins, amout))
	coins = []int{10}
	amout = 10
	fmt.Println(change(coins, amout))
}

输出:

4
0
1

注:创建一个长度为 amount+1 的一维数组 dp,并将 dp[0] 初始化为 1。然后对于每个硬币 coin,都遍历一遍 dp 数组,计算出所有能够凑到的 i 元的硬币组合数,并且使用这些组合数更新 dp[i]。最后返回 dp[amount] 即可。


🌟 每日一练刷题专栏 🌟

持续,努力奋斗做强刷题搬运工!

👍 点赞,你的认可是我坚持的动力! 

🌟 收藏,你的青睐是我努力的方向! 

评论,你的意见是我进步的财富!  

 主页:https://hannyang.blog.youkuaiyun.com/ 

Rust每日一练 专栏

(2023.5.16~)更新中...

Golang每日一练 专栏

(2023.3.11~)更新中...

Python每日一练 专栏

(2023.2.18~2023.5.18)暂停更

C/C++每日一练 专栏

(2023.2.18~2023.5.18)暂停更

Java每日一练 专栏

(2023.3.11~2023.5.18)暂停更

<think>嗯,用户让我介绍一下零钱兑换II的问题,并且要给出Golang的完整实现。首先,我得确认一下这个问题是什么。零钱兑换II应该就是那个经典的动态规划问题,也就是给定不同面额的硬币和一个总金额,计算可以凑成总金额的组合数,假设每种硬币有无限个可用。对吧?比如硬币有[1,2,5],总金额5,组合数是4种吗?或者可能我记错了例子,不过具体题目应该是这样。 那用户可能已经知道这个问题,但需要具体的解法,特别是用Golang实现。所以我要先回忆这个问题的解法思路。通常这类问题是用动态规划来解决的,状态转移方程是关键。可能的状态是dp[i][j],表示前i种硬币组成金额j的组合数。不过为了优化空间,通常会用一维数组,即dp[j],然后通过遍历硬币和金额来更新。 动态规划的递推关系式是什么呢?对于每个硬币coin,当处理到它的时候,对于金额从coin到总金额的每个j,dp[j] += dp[j - coin]。这样就能累积所有可能的组合数。这个过程需要先遍历硬币,然后遍历金额,以避免重复计算不同的排列,而只计算组合。比如,先使用硬币1,再使用硬币2,这样就不会出现像先2后1的情况被算作不同的组合,而是视为同一种组合。所以顺序很重要,必须先固定硬币的顺序,然后处理每个金额。 那Golang的实现应该怎么做呢?首先,我们需要一个dp数组,长度为amount+1。初始化dp[0]为1,因为凑成0元的方式只有一种,就是不选任何硬币。然后遍历每个硬币,对于每个硬币,从coin到amount依次更新dp[j] += dp[j - coin]。这样最后dp[amount]就是答案。 不过要考虑边界情况,比如amount为0的时候应该返回1吗?根据题目描述,可能要看具体题目要求,但通常题目会说当amount为0时返回1。比如示例中,当amount是0的情况。另外,如果硬币数组为空,而amount不为0的话,结果应该是0。 那现在我需要把这些逻辑用Golang代码写出来。结构大概是这样的:函数名为change,参数是amount int,coins []int,返回int类型。初始化dp数组,然后循环处理每个硬币,再内层循环处理金额。例如: func change(amount int, coins []int) int { dp := make([]int, amount+1) dp[0] = 1 for _, coin := range coins { for j := coin; j <= amount; j++ { dp[j] += dp[j - coin] } } return dp[amount] } 不过需要测试一下是否正确。比如,示例中的情况:coins = [1,2,5], amount=5。正确的组合数应该是4种?或者可能我之前记错了。或者可能另一个例子,比如coins = [1,2,5], amount=5的话,组合是: 1+1+1+1+1 1+1+1+2 1+2+2 5 所以确实是4种,那这个代码运行的结果是否正确?比如当coin=1时,每个j从1到5都会被处理,此时dp[j] += dp[j-1]。初始dp[0]=1,处理完coin=1后,dp数组每个j都是1,因为只有一种方式,用1分硬币。然后处理coin=2,j从2到5。例如,当j=2时,dp[2] += dp[0] → 1+1=2。这时候组合是两种情况:两个1或者一个2。接着处理coin=5的时候,j=5时,dp[5] += dp[0] → 原来的值加上1,所以最后dp[5]等于4。是的,这样结果正确。 那这样的代码应该是对的。不过需要考虑一些边界情况,比如amount=0时返回1,coins为空且amount>0时返回0。例如,当amount=0时,不管有没有coins,都返回1。那代码中的初始化已经处理了这个情况,因为dp[0]=1,如果amount是0的话,直接返回dp[0]就是1。 那测试一下其他情况,比如coins为空,amount=5,应该返回0。而如果coins是空的,循环不会执行,所以dp[5]还是初始值0,正确。另一个情况是硬币面额比amount大的情况,比如coin=3,amount=2,这个时候在循环时,j从3开始,但amount是2,所以内层循环不会执行,不会影响结果。 所以这个代码应该正确。那用户可能需要完整的代码,所以给出这个函数即可。另外,可能还需要一些注释或者解释,说明每个步骤的作用。比如解释为什么外层循环是硬币,内层循环是金额,这样可以保证组合而不是排列。 总结一下,用户的需求是得到零钱兑换II问题的Golang完整实现,所以代码如上述所示。需要注意的问题可能包括初始化dp数组,循环的顺序,以及如何处理不同的硬币和金额。最后返回dp[amount]即可。</think>以下是零钱兑换 II 问题的 Golang 完整实现,该问题要求计算用给定硬币凑出指定金额的组合数(假设每种硬币数量无限): ```go func change(amount int, coins []int) int { // 初始化动态规划数组,dp[i] 表示凑出金额 i 的组合数 dp := make([]int, amount+1) dp[0] = 1 // 金额为 0 时,组合数为 1(不选任何硬币) // 遍历每种硬币,保证组合数不重复计算不同顺序 for _, coin := range coins { // 从当前硬币面值开始更新 dp 数组 for j := coin; j <= amount; j++ { dp[j] += dp[j-coin] // 累加使用当前硬币的组合数 } } return dp[amount] } ``` ### 核心思路 1. **动态规划**:使用一维数组 `dp`,`dp[i]` 表示凑出金额 `i` 的组合数。 2. **外层循环硬币**:先遍历硬币种类,保证不同顺序的硬币组合不被重复计算(例如 `[1,2]` 和 `[2,1]` 视为同一种组合)。 3. **内层循环金额**:从当前硬币面值开始,更新 `dp[j]` 的值,累加使用当前硬币的组合数。 ### 示例说明 - **输入**:`amount = 5`, `coins = [1, 2, 5]` - **输出**:`4` - **解释**:组合方式为: - `1+1+1+1+1` - `1+1+1+2` - `1+2+2` - `5` ### 复杂度 - **时间复杂度**:O(N×M),其中 N 是金额,M 是硬币种类数。 - **空间复杂度**:O(N),仅需一维数组存储状态。 此解法通过动态规划高效地计算组合数,避免了重复计算,且代码简洁易懂。
评论 9
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hann Yang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值