目錄給定不同面額的硬幣 coins 和乙個總金額 amount。編寫乙個函式來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 -1。
示例 1:
輸入: coins = [1, 2, 5], amount = 11
輸出: 3
解釋: 11 = 5 + 5 + 1
示例 2:
輸入: coins = [2], amount = 3
輸出: -1
說明:
與斐波那契數列類似,不過我們需要自行總結狀態轉移方程
。因為各種面值的硬幣數量不限,所以我們認為在總數額變化時,各硬幣數目的子問題間是相對獨立的,符合最優子結構。
最終狀態轉移方程
如下:
\[d p(n)=\left\
0, n=0 \\
-1, n<0 \\
\min \)+1 \mid \operatorname \in \operatorname s\}, n>0
\end\right.\]
事先說明這種方法提交會出現超時錯誤,這裡拋磚引用,後面對其進行優化。
我們按照之前總結的狀態轉移方程
進行編寫,**如下:
class solution
//考慮ans自始至終未更新,仍未初始值的錯誤情況
return ans==amount+1?-1:ans;
// if(ans == amount+1)
// return -1;
// else return ans;
}};
同斐波那契數列,為了減少子問題重複計算增加時間複雜度的問題,我們嘗試使用乙個數值或者雜湊表對已經計算的子問題硬幣數量進行記錄:
**如下:
class solution
private:
int help(vector& dp, int amount,vector& coins)
dp[amount] = res == (amount+1)? -1 : res;
return dp[amount] ;
// if(res != amount+1)else return -1;
}};
**中需要注意的是:
同斐波那契方法,既然滿足最優子結構。我們從base case開始,自底向上求得逐層子問題的硬幣個數。有一點需要注意的是,記錄子問題結果的vector容器初始化為amount+1,這是由於dp[amount]
最大不可能超過amount
,所以amount + 1
就是乙個無意義的數了。
最小面值為 1 元
class solution
}return (res[amount]!=amount+1)?res[amount]:-1;
}};
從貪心演算法的角度考慮,想要總硬幣數最少,肯定是優先用大面值硬幣.所以對 coins 按從大到小排序:
sort(coins.rbegin(), coins.rend());
以coins下標為回溯條件,根據硬幣種類進行一一篩選。
for (int k = amount / coins[index]; k + count < ans && k >= 0; k--)
先丟大硬幣,當當前選擇面值的個數總和超過總額時,就可以遞迴下一層丟的是稍小面值的硬幣。可以看到這裡是從amount / coins[index]
進行反向遍歷的。可以理解為乘法對完全揹包問題的加速,用乘法算一下最多能丟幾個。而之後的amount - k * coins[c_index]
表示減去扔了 k 個硬幣後的剩餘數額大小。count + k
表示當前硬幣個數(又新增了k 個硬幣)。
需要注意的是,貪心最先找到的不一定是最優解。考慮到有[1,7,10]
這種用例,按照貪心思路10 + 1 + 1 + 1 + 1
會比7 + 7
更早找到。所以還是需要把所有情況都遞迴完。
這樣進行遞迴其實運算量很大,因此採用剪枝進行優化。具體實現在for迴圈的條件裡:
for (int k = amount / coins[index]; k + count < ans && k >= 0; k--)
其中k + count
表示當前還剩amount - k * coins[index]
數額的面值時,已經使用的硬幣數量,我們將該當前硬幣數量與最小硬幣數量進行比較,如果k + count >= ans
,即使剩餘的amount為0,也不會對最小的硬幣數量進行更新,所以沒有繼續進行回溯的必要,直接進行剪枝即可。
完整**如下:
class solution
// 如果硬幣種類遍歷完,也需要停止回溯
if (index == coins.size())
return;
// 對於不同面額的硬幣
// 直接乘法加速貪心過程
for (int k = amount / coins[index]; k + count < ans && k >= 0; k--)
}int coinchange(vector& coins, int amount)
};
322 零錢兌換
class solution 不是大的取的越多越好,大的取的很多,最後不能剛好取到,比如22,陣列是10,6,你直接取兩個10肯定不行第乙個數有取1個,取0個或者取多個好幾種取法。比如18,你有10和6,那麼10乙個都不能取 所以要考慮的只是當前這一位可以取幾個 for int i left coi...
322 零錢兌換
最值問題 最優子結構特性 子問題重疊特性 使用動態規劃 狀態轉移方程 列出狀態轉移方程 步驟 1.明確狀態 原問題和子問題中變化的量 amount 連續性變化 2.定義動態規劃陣列 函式的含義 dp n 的含義 湊成n需要的硬幣個數 3.明確選擇 對於每個狀態,可以做出什麼選擇來改變當前狀態 選乙個...
322 零錢兌換
給定不同面額的硬幣 coins 和乙個總金額 amount。編寫乙個函式來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 1。示例 1 輸入 coins 1,2,5 amount 11 輸出 3 解釋 11 5 5 1 示例 2 輸入 coins 2 amount...