力扣322——零錢兌換

這道題主要涉及動態規劃,利用這個,就能很好解決這個問題。
<!-- more -->java

原題

給定不一樣面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算能夠湊成總金額所需的最少的硬幣個數。若是沒有任何一種硬幣組合能組成總金額,返回 -1。git

示例 1:github

輸入: coins = [1, 2, 5], amount = 11
輸出: 3 
解釋: 11 = 5 + 5 + 1

示例 2:segmentfault

輸入: coins = [2], amount = 3
輸出: -1

說明:函數

你能夠認爲每種硬幣的數量是無限的。學習

原題url:https://leetcode-cn.com/probl...優化

解題

求出全部可能

咱們能夠從小到大,求出由當前硬幣,組成全部金額的最小數,這樣最終就是最大金額所能組成的最小硬幣數量。url

這種方法核心思想就是記錄全部中間狀態的結果,若是在實際使用中,你的傳入參數amount是不斷變化的,那麼用這種方法會比較方法,由於以前的結果能夠被重複利用,這樣也是一種優點。spa

如今咱們來看看代碼:code

class Solution {
    public int coinChange(int[] coins, int amount) {
        // 排序,升序
        Arrays.sort(coins);

        // 用來記錄中間結果,各類金額所須要的硬幣數量
        int[] result = new int[amount + 1];
        result[0] = 0;
        // 遍歷全部可能的金額
        for (int currentAmount = 1; currentAmount <= amount; currentAmount++) {
            int minNum = Integer.MAX_VALUE;
            // 遍歷全部硬幣
            for (int j = 0; j < coins.length; j++) {
                // 當前金額已經比硬幣的值小
                int remainAmount = currentAmount - coins[j];
                if (remainAmount < 0) {
                    break;
                }

                // remainAmount沒法由硬幣組成
                if (result[remainAmount] == Integer.MAX_VALUE) {
                    continue;
                }

                // 取更小的值
                minNum = Math.min(minNum, result[remainAmount] + 1);
            }

            result[currentAmount] = minNum;
        }

        return result[amount] == Integer.MAX_VALUE ? -1 : result[amount];
    }
}

提交OK,執行用時:12 ms,內存消耗:36 MB,只超過了83.24%的 java 提交,看來還有優化的空間。

動態規劃優化

倒不是說動態規劃就必定比上面的方法更加優秀,只是也是一種思想,能夠利用 dfs(深度優先搜索) 或者 bfs(廣度優先搜索),我下面這種寫法是 dfs,由於是一路進行到底以後,再去考慮其餘狀況的。(補充一點,若是使用 bfs 的話,能夠藉助隊列來實現非遞歸的形式。)

所謂的優化,就是從硬幣的使用上來講,從面值大的開始,而且從可使用數量最大的開始。與此同時,咱們也記錄了最小使用數量,若是用當前面值最大的硬幣而且使用最多時,依舊大於最小值,那麼就不用繼續查找了。

以上的優化,實際上是和題目相關聯,雖然不能用在其餘的題目上,但也能夠做爲一種思想,值得咱們借鑑和學習。

接下來咱們看看代碼:

class Solution {
    private int minCount = Integer.MAX_VALUE;

    public int coinChange(int[] coins, int amount) {
        // 排序
        Arrays.sort(coins);
        // 從大往小找
        helper(coins, coins.length - 1, 0, amount);
        return minCount == Integer.MAX_VALUE ? -1 : minCount;
    }

    private void helper(int[] coins, int coinIndex, int curCount, int amount) {
        if (coinIndex < 0) {
            return;
        }
        // 若是能整除,直接取完
        if (amount % coins[coinIndex] == 0) {
            minCount = Math.min(minCount, curCount + amount / coins[coinIndex]);
            return;
        }

        // 數量上也是從大往小找
        for (int i = amount / coins[coinIndex]; i >= 0; i--) {
            // 由於接下來至少還會用1個幣
            if (curCount + i + 1 >= minCount) {
                break;
            }
            
            helper(coins, coinIndex - 1, curCount + i, amount - i * coins[coinIndex]);
        }
    }
}

提交OK,執行用時:2 ms,內存消耗:34.7 MB,超過了100.00%的 java 提交,有種又快又好的感受。

總結

以上就是這道題目個人解答過程了,不知道你們是否理解了。這道題主要利用動態規劃就能夠解決,優化的時候須要注意邊界條件,從大到小取值,在時間複雜度上能更加優化。

有興趣的話能夠訪問個人博客或者關注個人公衆號、頭條號,說不定會有意外的驚喜。

https://death00.github.io/

公衆號:健程之道

相關文章
相關標籤/搜索