1. 程式人生 > >2.零錢兌換

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];
    }
}