算法—leetcode—322

124 阅读1分钟

题目

(322). 零钱兑换

题目描述

给定不同面额的硬币coins和一个总金额amount;编写一个函数来计算可以凑成总金额所需的最少的硬币个数;如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。

示例

示例一

输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1

示例二

输入:coins = [2], amount = 3
输出:-1

思路

明确base case -> 明确【状态】 -> 明确【选择】 -> 定义DP数组/函数的含义

base case:amount=0,则返回0,不需要任何硬币就满足条件

状态:amount的逐渐先base case靠近,原问题与子问题中变化的量

选择:导致状态发生变化的行为,选择一枚硬币,状态(amount)就会变化

定义DP函数/数组的含义:输入一个目标金额n,返回凑出目标金额n的最少硬币数量。

动态规划解题套路框架

代码

package leetcode

import "log"

// coinChange
// 零钱兑换
// stack overflow
func coinChange(coins []int, amount int) int {
	// 求最小值,所以初始化为正无穷
	maxInt := 1<<31 - 1

	var dp func(amount int) int
	dp = func(amount int) int {
		if amount == 0 {
			return 0
		}
		if amount < 0 {
			return -1
		}
		num := maxInt
		// [1, 2, 5]
		// 11
		for _, coin := range coins {
			// 子问题
			subProblem := dp(amount - coin)
			if subProblem == -1 {
				continue
			}
			num = min(num, 1+subProblem)
		}

		if num == maxInt {
			return -1
		} else {
			return num
		}
	}

	return dp(amount)
}

// min
// 获取两个整型最小值
func min(a, b int) int {
	if a > b {
		return b
	}
	return a
}

// coinChange
// 零钱兑换
// 通过备忘录解决重叠子问题
func coinChangeV2(coins []int, amount int) int {
	// 求最小值,所以初始化为正无穷
	maxInt := 1<<31 - 1
	data := map[int]int{}

	var dp func(amount int) int
	dp = func(amount int) int {
		// 从备忘录中获取
		if _, ok := data[amount]; ok {
			return data[amount]
		}
		if amount == 0 {
			return 0
		}
		if amount < 0 {
			return -1
		}
		num := maxInt
		// [1, 2, 5]
		// 11
		for _, coin := range coins {
			// 子问题
			subProblem := dp(amount - coin)
			if subProblem == -1 {
				continue
			}
			num = min(num, 1+subProblem)
		}

		if num == maxInt {
			data[amount] = -1
		} else {
			data[amount] = num
		}
		return data[amount]
	}

	return dp(amount)
}

// coinChange
// 零钱兑换
// 通过备DP table解决重叠子问题
// 超出时间限制
func coinChangeV3(coins []int, amount int) int {
	// dp := make([]int, amount+1)
	dp := map[int]int{}
	// base case
	dp[0] = 0
	number := amount + 1
	// 外层for循环在遍历所有状态的所有取值
	// [2,5,10,1] 27
	for i := 0; i < number; i++ {
		// 内层for缓存在求所有选择的最小值
		for _, coin := range coins {
			// 子问题无解
			if (i - coin) < 0 {
				continue
			}
			// 设置默认值
			if _, ok := dp[i]; !ok {
				dp[i] = number
			}
			if _, ok := dp[i-coin]; ok {
				dp[i] = min(dp[i], 1+dp[i-coin])
			}
			log.Println(i, dp[i])
		}
	}

	if rs, ok := dp[amount]; !ok || rs == number {
		return -1
	} else {
		return dp[amount]
	}
}

leetcode提交记录

参考

来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/co…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。