Leetcode 322. 零錢兌換【動態規劃&貪心算法+回溯】

問題描述

給定不同面額的硬幣 coinscoins 和一個總金額 amountamount。編寫一個函數來計算可以湊成總金額所需的最小的硬幣個數、如果沒有任何一種硬幣組合能組成總金額,返回 1-1
輸入:coins=[1,2,5],amount=11coins=[1, 2, 5], amount=11
輸出: 33
解釋:11=5+5+111=5+5+1
每種硬幣的數量是無限的

解題報告

動態規劃

dp[i]dp[i] 表示組合成面額爲 ii 的硬幣需要的硬幣個數,則:

dp[i]=min(dp[i],dp[icoin[j]]+1)      j[0,1,,n] dp[i]=min(dp[i], dp[i-coin[j]]+1) \;\;\;j 取值[0, 1, \cdots, n]

貪心+回溯

這種實現方式源自題解區:lkaruga
貪心思想
爲了使得總硬幣數目最少,優先使用大面值的硬幣,所以對 coinscoins 進行逆序排序;
回溯思想
如果首先將大面值的硬幣使用了,則可能無法湊出目標值,所以需要進行回溯,依次減少大面值的硬幣數量

注意:最先找到不一定是最優解,所以其他情況我們也需要考慮。考慮其他情況時,合理使用剪枝,可以減少時間消耗。

實現代碼

動態規劃實現

int coinChange(vector<int>&coins,int amount){
        vector<int>dp(amount+1,amount+1);
        dp[0]=0;
        for(int i=1;i<=amount;i++){
            for(int j=0;j<coins.size();j++){
                if(i>=coins[j]){
                    dp[i]=min(dp[i],dp[i-coins[j]]+1);
                }
            }
        }
        return (dp[amount]==amount+1)?-1:dp[amount];
    }
};

貪心+回溯實現

class Solution{
    public:
        int coinChange(vector<int>&coins,int amount){
            sort(coins.begin(), coins.end(),greater<int>());
            int ans=INT_MAX;
            dfs(coins, amount, 0, 0, ans);
            return ans==INT_MAX?-1:ans;
        }
        void dfs(vector<int>&coins,int amount, int i, int count,int& ans){
            if(amount==0){
                ans=min(ans, count);
                return;
            }
            if(i==coins.size()) return;
            for(int k=amount/coins[i];k>=0&&k+count<ans;k--){
                dfs(coins, amount-k*coins[i], i+1, count+k,ans);
            }
        }
};

參考資料

[1] Leetcode 322. 零錢兌換
[2] 題解區:lkaruga

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章