题目:
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。你可以认为每种硬币的数量是无限的。
解答:这里我们以第一个示例演示,分别画出每种金额的树形结构,如下
这里我们采取一种自下而上的方法,这里我重点解释代码中一个公式:
dp[i]=min(dp[i],dp[i-e]+1);
dp[i]:表示不同的金额下所需最少的钱币数
dp[i-e]+1:这里i-e表示当前金额减去一种面值后金额,dp[i-e]的值一定代表i-e时所需要的最小的钱币数(这个值我们之前一定算过),因为最开始就是从金额是0开始递增来计算不同金额所需的最小钱币数,这里的e表示不同的面值的大小,正因为我们减去了一枚钱币,所以后面要加个1,所以dp[i-e]+1表示的就是当前所耗费的钱币数啊!!!
dp[]是一个数组,并且数组里的值都被表示为很大的数,dp[i-e]+1表示钱币数一定会代替当前的dp[i],没错吧,之后我们计算每个dp[i-e]+1的数量,将最小的保存下来就可以啦!最后在计算大一个金额的,以此类推。
可以结合上面的树形图去观察,会发现每个树杈下面都是之前算过的,其实就是在遍历每个树杈子,例如 计算amount =4时,我们直接就比较3 和 2所用的钱币数就可以啦(其实就是dp[2]和dp[3]),不用重复计算,这就是自下而上的好处!!
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
if(amount==0) return 0;
int dp[10005];
for(auto& i: dp) i=10006;
dp[0] = 0;
//这一步求出我们要计算金额之前每种金额需要的最少钱币数
for(int i=0; i<=amount; i++) {
//这里是遍历每个硬币的面值
for(auto& e: coins) {
//用总金额-硬币面值
if(i-e>=0)
dp[i]=min(dp[i],dp[i-e]+1);
}
}
return dp[amount]>=10006? -1: dp[amount];
}
};