1. 程式人生 > >LeetCode-【動態規劃】-零錢兌換

LeetCode-【動態規劃】-零錢兌換

給定不同面額的硬幣 coins 和一個總金額 amount。編寫一個函式來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 -1

示例 1:

輸入: coins = [1, 2, 5], amount = 11

輸出: 3
 
解釋: 11 = 5 + 5 + 1

示例 2:

輸入: coins = [2], amount = 3

輸出: -1

說明:
你可以認為每種硬幣的數量是無限的。

題解:呃,第一想法是遞迴加回溯找到組合最小值,又來了,剛寫的組合總和 Ⅳ的第一想法就是遞迴加回溯,不過結局比較慘,時間超限了。沒有意外,本題在[1,2,5] 100這條資料就超時了,總共過了15條資料,呵呵,有時候思路正確並不一定能完美解題。好吧,還是動規登場,假設dp[i]為錢數為i時需要的最少硬幣數,當i=0時,dp[0]=0,這個毫無疑問,當i>0時,直接說不好理解,那就舉個例項,[1,2,3] 3 對這個例子來說,一種情況是隻用1,數量是3,一種是用1和2,數量是2,最後是隻用3,數量是1,那問題變成了,怎樣使最終的結果為1,你可能說比較,好,誰和誰比較,呃。。。,換一個角度思考,對任意硬幣coins[j],我們有使用和不使用兩種情況,所以比較的雙方出來了,dp[i]代表使用coins[j],dp[i-coins[j]]+1代表不使用coins[j],那麼這裡為什麼又要加1呢,因為對i-coins[j]來說它加上一個coins[j]就是i了,所以這裡需要加上1,然後i-coins[j]再從前一個狀態判斷最小值,最後直到已知值dp[0]。

其實真正的過程是i從0到amount記錄每個狀態的最小值,最後執行到dp[amount]時就是最後求的最小值,這也是動規真正的執行過程,不過我們習慣從最後開始分析問題,通過確定最後的狀態與之前狀態的關係,從而確定動態轉移方程。

class Solution {
    public int coinChange(int[] coins, int amount) {
        Arrays.sort(coins);
        int n=coins.length;
        if(n==0||amount==0)
            return 0;
        int[] dp=new int[amount+1];
        Arrays.fill(dp,amount+1);
        dp[0]=0;
        for(int j=0;j<n;j++){
            for(int i=coins[j];i<=amount;i++){
                dp[i]=Math.min(dp[i],dp[i-coins[j]]+1);
            }
        }
        return dp[amount]>amount?-1:dp[amount];
    }
}