给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
- 判断是最值问题,解题过程可以枚举,考虑动态规划;
- 思考最后一步:加入一枚面值x的硬币,刚好凑齐总金额;硬币个数+1;
- 定义状态数组 dp[i]:凑成金额i所需要的硬币个数
- 推导状态方程,最后使用的硬币面值为x,则dp[i] = min(dp[i-x])+1, x in coins.
- 确定边界条件:dp[0] = 0
- 确定状态数组初值: dp[i] = amount / 1 + 1 在初值和边界值基础上进行迭代。
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
/* 简单场景 */
if(amount == 0 || coins.empty()) {
return 0;
}
/* 硬币面值最小1元,则次数最多 (amount/1) 次 */
int max_cnt = amount / 1;
int choice_cnt = coins.size();
/* 直接以 面值v 作为问题规模,定义状态:正好凑成 面值v 的最小次数为 min_cnt[v] 则问题的解为 min_cnt[amount];
* */
/* 初始化状态数组,最终解为 min_cnt[amount],起始为min_cnt[0],故状态数组长度为 amount+1 ; 已知合乎题意的最大次数为 max_cnt , 故初始化为一个不可达值 max_cnt+1 */
vector<int> min_cnt(amount+1, max_cnt+1);
min_cnt[0] = 0;
for(int v = 1; v <= amount; v++) {
for(int j = 0; j < choice_cnt; j++) {
int choice = coins[j];
/* 只要没有超出设置当前要求面值,则可选取该枚硬币试试看 */
if(v - choice >= 0) {
min_cnt[v] = min(min_cnt[v], min_cnt[v-choice]+1);
}
}
}
/* 最小次数没更新过,说明无法凑成该面值 */
if(min_cnt[amount] == max_cnt+1) {
return -1;
}
return min_cnt[amount];
}
};