一,问题描述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]; }