一,問題描述html
給定一組硬幣數,找出一組最少的硬幣數,來找換零錢N。ios
這類問題因爲給定的硬幣面值與數量的不一樣,可能演化出不少種不一樣的版本,這裏先講最簡單的兩種形式。算法
二,貪婪法求解硬幣找零問題數組
貪婪法的思路很簡單,不斷地從總找零值裏減去面值最大的硬幣。若是找零的值小於最大的硬幣值,則嘗試第二大的硬幣,依次類推。ide
C++代碼實現以下:spa
1 #include <iostream> 2 3 using namespace std; 4 5 int main() 6 { 7 int coin_num, N; //硬幣面值數量,須要找零的錢數 8 cin>>coin_num>>N; 9 int coin[coin_num];//不一樣的硬幣面值 10 for (int i = 0; i < coin_num; ++i) 11 cin>>coin[i]; 12 13 int total_num = 0, cur_coin = coin_num - 1; 14 while (N!=0) 15 { 16 total_num += N / coin[cur_coin]; //從最大的硬幣面額開始,從總錢數裏面扣除 17 N %= coin[cur_coin]; 18 cur_coin--; 19 } 20 cout<<total_num<<endl; 21 }
可是使用貪婪法求解硬幣找零問題,必需要讓給定的硬幣面值知足必定的先決條件,不然求出的解不是最優解!code
正例:htm
給定一組硬幣:1,5,10,20。N=30。求最小硬幣數。blog
答案顯然是2,由於先考慮25,剩5,取一個5元。最後結果爲2。ci
考慮以下一個例子:
給定一組硬幣:1,5,20,25。N=40。求最小硬幣數。
若是用貪婪法,先考慮25,剩餘15,取3個5元。最後結果爲4。
可是很顯然,若是直接使用20的硬幣,則2個便可。因而可知,貪婪法的解在這個問題裏面並非最優的。對於這種狀況,就須要使用動態規劃進行求解。
而使用貪婪法可以求出最優解的條件是:每一個大的面值,都是小的面值的整數倍。
從上面的正例來看, 20是10,5,1的整數倍,同理10是5,1的整數倍,以此類推。而在反例中,25不是20的整數倍。
證實方法能夠見這篇博客,博主的證實很是詳細。
貪婪算法硬幣找零最優解問題證實:https://www.cnblogs.com/organic/p/6151702.html
而在這裏咱們只體會一下緣由:只有在大的面額是小的面額的整數倍的狀況下,先找大的金額才必定時最優的。例如20是10的倍數,則全部能用20抵扣的金額,都必定比用10來抵扣花的數量少。
反之則不必定。例如25和20,在湊40時,兩個20便可,而25還須要和更小的金額搭配。
二,動態規劃求解硬幣找零問題
根據動態規劃的思想,咱們將給N找零的問題分解成「給更小金額找零」的子問題。最後,利用小面值目標的解,去得出大面值目標的解。具體實現,首先是先從小面值的找零開始,將解決方案依次存入動態規劃數組,一直往大面值累加,最後直接輸出動態規劃數組指定位置的值也即大面值找零所需的最少硬幣數,算法實現以下:
int coinChange(vector<int>& coins, int amount) { int n = coins.size(); if (n==0) return -1; if (amount == 0) return 0; int coin_num[amount+1] = {0}; for (int i = 0; i < n; ++i) { if (coins[i] <= amount) coin_num[coins[i]] = 1; } for (int i = 1; i <=amount; ++i) { for (int j = 0; j < n; ++j) { if (i - coins[j] >=0 && coin_num[i-coins[j]] > 0) { if (coin_num[i] == 0) coin_num[i] = coin_num[i-coins[j]] + 1; else coin_num[i] = min(coin_num[i], coin_num[i-coins[j]] + 1); } } } if (coin_num[amount] == 0) return -1; else return coin_num[amount]; }