LeetCode 热题 100_零钱兑换(85_322)
- 题目描述:
- 输入输出样例:
- 题解:
- 解题思路:
- 思路一(动态规划):
- 代码实现
- 代码实现(思路一(动态规划)):
- 以思路一为例进行调试
题目描述:
给你一个整数数组 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] <= 231 - 1
0 <= amount <= 104
题解:
解题思路:
思路一(动态规划):
1、通过题目的描述需解决凑成总金额所需的最少的硬币个数,且硬币可以重复使用,这样自然而然将问题转换为了完全背包问题(金币的数量为物品的个数,金额为物品的价值)。
2、具体思路如下:
① dp[j],dp数组的含义为:放满j容量的背包,最少的物品数量。
② dp[j]=min(dp[j],dp[j-nums[i]]+1) ,等号右侧 dp[ j ]代表不放物品 i , dp[j-nums[i]]+1 代表放物品 j 。
③ dp[0]=0,因容量为 0 的背包可以放 0 件物品
④ 因取最小值,其他的dp数组的元素值为INT_MAX,数以这里需考虑溢出的情况dp[j-nums[i]]若为INT_MAX再加一则会溢出
⑤ 因可重复使用同一金额的硬币则遍历顺序为从左到右。
3、复杂度分析:
① 时间复杂度:O(Sn),其中 S 是金额,n 是面额数(物品的价值)。我们一共需要计算 O(S) 个状态,S 为题目所给的总金额(背包容量)。双重循环遍历 物品 和 背包。
② 空间复杂度:O(S)。dp 所需的空间 。
代码实现
代码实现(思路一(动态规划)):
class Solution{
public:// 定义 coinChange 函数,参数为硬币面值数组 coins 和目标金额 amountint coinChange(vector<int> &coins, int amount){// 初始化 dp 数组,大小为 amount + 1(从 0 到 amount),并将所有元素初始化为 INT_MAX// dp[i] 表示凑成金额 i 所需要的最少硬币数量vector<int> dp(amount + 1, INT_MAX);// dp[0] = 0,因为凑成金额 0 不需要任何硬币dp[0] = 0;// 外层循环遍历每种硬币(物品)for (int i = 0; i < coins.size(); i++){// 内层循环遍历从当前硬币面值到目标金额之间的所有金额(背包)for (int j = coins[i]; j <= amount; j++){// 防止数据溢出:只有当 dp[j - coins[i]] 不等于 INT_MAX 时,才进行更新// 这是因为如果 dp[j - coins[i]] 为 INT_MAX,表示无法凑成金额 j - coins[i]if (dp[j - coins[i]] != INT_MAX) {// 更新 dp[j]:表示使用当前硬币后,凑成金额 j 的最小硬币数dp[j] = min(dp[j], dp[j - coins[i]] + 1);}}}// 如果 dp[amount] 还是 INT_MAX,表示无法凑成目标金额,返回 -1// 否则返回 dp[amount],即凑成目标金额所需要的最少硬币数量return dp[amount] == INT_MAX ? -1 : dp[amount];}
};
以思路一为例进行调试
#include<iostream>
#include<vector>
using namespace std;class Solution{
public:// 定义 coinChange 函数,参数为硬币面值数组 coins 和目标金额 amountint coinChange(vector<int> &coins, int amount){// 初始化 dp 数组,大小为 amount + 1(从 0 到 amount),并将所有元素初始化为 INT_MAX// dp[i] 表示凑成金额 i 所需要的最少硬币数量vector<int> dp(amount + 1, INT_MAX);// dp[0] = 0,因为凑成金额 0 不需要任何硬币dp[0] = 0;// 外层循环遍历每种硬币(物品)for (int i = 0; i < coins.size(); i++){// 内层循环遍历从当前硬币面值到目标金额之间的所有金额(背包)for (int j = coins[i]; j <= amount; j++){// 防止数据溢出:只有当 dp[j - coins[i]] 不等于 INT_MAX 时,才进行更新// 这是因为如果 dp[j - coins[i]] 为 INT_MAX,表示无法凑成金额 j - coins[i]if (dp[j - coins[i]] != INT_MAX) {// 更新 dp[j]:表示使用当前硬币后,凑成金额 j 的最小硬币数dp[j] = min(dp[j], dp[j - coins[i]] + 1);}}}// 如果 dp[amount] 还是 INT_MAX,表示无法凑成目标金额,返回 -1// 否则返回 dp[amount],即凑成目标金额所需要的最少硬币数量return dp[amount] == INT_MAX ? -1 : dp[amount];}
};int main(int argc, char const *argv[])
{// 给定硬币面值数组和目标金额vector<int> coins = {1, 2, 5};int amount = 11;// 创建 Solution 对象Solution s;// 调用 coinChange 函数并输出结果cout << s.coinChange(coins, amount);return 0;
}
LeetCode 热题 100_零钱兑换(85_322)原题链接
欢迎大家和我沟通交流(✿◠‿◠)