原题链接
题目描述
给你一个整数数组 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
示例 4:
输入:coins = [1], amount = 1
输出:1
示例 5:
输入:coins = [1], amount = 2
输出:2
问题分析
总体思路和之前讲的 518. Coin Change 2 一样,可以先去看那篇文章。
这里就说一下dp数组的默认值,注意,由于最后不能保证一定能找到某种组合(找不到返回-1),所以一开始dp数组应该默认为一个特定的值,注意到状态转移方程使用了min操作,所以这个默认值应该初始化为一个比较大的值,一开始我想直接初始化为INT_MAX,这样在最后计算完成之后,如果dp[i][j]为INT_MAX,就表示不存在相应的组合。
但是,我们又注意到状态转移方程中存在 dp[i][j-coins[i]] + 1 ,如果一开始的默认值为INT_MAX,则可能发生溢出现象,那么这里取一个什么值合适呢,注意到coins数组中的元素最小值是1,当全部使用1来组成最终的amount时,会用到amount个1,也就是说,对于任意的coins数组,最后如果存在解,那么所花硬币数一定小于amount+1,故一开始dp数组都初始化为amount+1,起到了和INT_MAX的标记作用,但是又不会造成溢出。
代码
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
int n = coins.size();
vector<vector<int>> dp(n, vector<int>(amount+1, amount+1));
//dp[i][j]表示可选硬币为0...i时,能组成j所需的最少硬币
for(int i = 0; i < n; i++) dp[i][0] = 0;
for(int j = 1; j <= amount; j++) {
if(j >= coins[0] && j % coins[0] == 0) dp[0][j] = j/coins[0];
}
for(int i = 1; i < n; i++){
for(int j = 1; j <= amount; j++){
if(j >= coins[i]) dp[i][j] = min(dp[i-1][j], dp[i][j-coins[i]] + 1);
else dp[i][j] = dp[i-1][j];
}
}
return dp[n-1][amount] == amount+1 ? -1 : dp[n-1][amount];
}
};