2.零錢兌換
給定不同面額的硬幣 coins 和一個總金額 amount。編寫一個函式來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 -1
。
示例 1:
輸入: coins =[1, 2, 5]
, amount =11
輸出:3
解釋: 11 = 5 + 5 + 1
示例 2:
輸入: coins =[2]
, amount =3
輸出: -1
說明:
你可以認為每種硬幣的數量是無限的。
在刷動態規劃經典例題——數字三角形之前,我已嘗試解答這個問題。
用的是深度優先遍歷+貪心,先拿完所有最大面額的硬幣,剩下的錢從剩下的硬幣中一一選取
這樣算耗時大,還算不對。
瞭解了動態規劃後,過了幾天又看到這題,才想起來可以用動態規劃
分析:
(1)步驟拆分:
因為n為整數,所以每個步驟找零差值為1元,即:
求兌換n元的最小硬幣,可以先求兌換n-1元最少硬幣數量
求兌換n-1元,要先求兌換n-2元
。。。
一路下去,可以拆分成如下:
1、兌換0元(為了方便,所以從0開始)最少硬幣數量
2、兌換1元最少硬幣數量
3、兌換2元最少硬幣數量
。。。
n+1、兌換n元最少硬幣數量
設陣列dp[n],且 dp[i] 為兌換 i 元所用最少硬幣數量(所以要從0開始,dp[0]是兌換0元的最少硬幣數量,多麼直觀啊)
(2)狀態轉移方程分析:
每一步找零差值為1,硬幣面額為整且不小於1,設dp[0]=0(原因見示例分析);
如果i元可以找零,那麼一定有
找零i元 = 找零j元 + 一枚m元硬幣 (j<i,j元可以找零 , m 屬於陣列coins)
比如 : 找零5元 等於 找零3元 + 一枚2元硬幣
找零5元 等於 找零5元 + 一枚5元硬幣
設int b為該方案找零硬幣數
則 b = dp[j]+1 (1表示1枚硬幣)
當然滿足 i =j+m 的j和m很多,b也很多,
遍歷0~i-1裡面可以找零的及陣列coins,找出這樣的 j 和 m ,求出所有的b
我們選出最小的b作為最優解dp[i]即可
若一個j和m也沒有,記為-1,表示實在無法找零
狀態轉移方程不言而喻
(3)示例分析:
假設硬幣面額陣列c[3]={2,3,5},找零11元
找零 i 元 = 找零 j 元 +1枚m面額硬幣 簡寫為:
z[i]=z[j]+m;
int b=某方案找零硬幣個數,
dp[i]=找零 i 元最少硬幣數量
1、0元:不存在,但是為了計算找零=硬幣面額這種情況,設dp[0]=0
2、1元:同上,dp[1]=-1;
3、2元:2元可以用一枚2元硬幣找零,即 z[2] = z[0]+2,所以dp[2]=1;
4、3元:z[3]=z[0]+3; 所以 dp[3]=1
5、4元:z[4]=z[2]+2; 所以 dp[4]=dp[2]+1=1+1=2
6、5元:有3種找零方法:
①z[5]=z[0]+5; b=0+1=1; ②z[5]=z[2]+3; b=dp[2]+1=2; ③z[5]=z[3]+2; b=dp[3]+1=2;
方案①的b最少,所以 dp[5]=1;
7、6元:兩種方法:
①z[6]=z[3]+3,b=dp[2]+1=2 ②z[6]=z[4]+2,b=dp[4]+1=3
dp[6]=2;
8、7元:三種:①z[7]=z[2]+5 b=2 ②z[7]=z[4]+3 b=3 ③z[7]=z[5]+2 b=2
所以dp[7]=2
9、8元:兩種:①z[8]=z[5]+3 b=2 ②z[9]=z[6]+2 b=2
所以dp[8]=2
10、9元:兩種:①z[9]=z[4]+5 b=3 ②z[9]=z[6]+3 b=3 ③z[9]=z[7]+2 b=3
dp[9]=3
11、10元:三種:①z[10]=z[5]+5 b=2 ②z[10]=z[7]+3 b=3 ③z[10]=z[8]+2 b=3
dp[10]=2
12、11元(最終問題):三種:①z[11]=z[6]+5 b=3 ②z[11]=z[8]+3 b=3 ③z[11]=z[9]+2 b=4
dp[11]=3
所以找零11元最少要3個硬幣
改造一下還能求出是哪三個硬幣
本題程式碼
class Solution {
public int coinChange(int[] coins, int amount) {//硬幣陣列,找零面額
if(amount==0){
return 0;
}
int len=coins.length;
int []dp=new int[amount+1];
for(int p=1;p<amount+1;++p){
dp[p]=p+1;
}
dp[0]=0;
for(int i=1;i<=amount;++i){
int h=amount+1;
for(int j=0;j<len;++j){
if(coins[j]<=i && dp[i-coins[j]]!=-1){
if(dp[i-coins[j]]<=h){
h=dp[i-coins[j]];
}
}
}
if(h<i+1){
dp[i]=h+1;
}else{dp[i]=-1;}
}
return dp[amount];
}
}