问题:
给定一组硬币面值coins,和一个总价amount
求最少用多少个硬币能构成总价,若无法构成,返回-1
Example 1: Input: coins = [1, 2, 5], amount = 11 Output: 3 Explanation: 11 = 5 + 5 + 1 Example 2: Input: coins = [2], amount = 3 Output: -1 Note: You may assume that you have an infinite number of each kind of coin.
解法:
解法一:DP(动态规划) Unbounded knapsack problem(完全背包问题)
dp[i]:要构成总价为 i ,最少需要的硬币数。
动态转移方程:dp[i] = min{ (j:0~coins.size) ( dp[i-coins[j]]+1 ) }
推导:考虑最后一步状态,
只可能为:前一个状态(构成总价为 i-coins[j] 的最少硬币数)+ 一个硬币(coins[j])
我们要找最少硬币数,则求coins各种情况下的解的最小值。
边界:
dp[0] = 0
初始化:
dp[i] = amount+1
设为最大值:coin为1,构成amount总价则需要amount个硬币。再+1,设为不可能取到的最大值。
由于状态转移方程为dp+1,再取min,
我们设定的初始值dp=amount+1,
状态转移方程中,各个状态的dp都为初始值(未赋值的非法值时)min(dp+1,dp)则一定还为这个不可能取到的最大值。
代码参考:
1 class Solution { 2 public: 3 //sub-problem: 4 //dp[s] = min(dp[s-coins[j]]+1) 5 //base:dp[0] = 0 6 int coinChange(vector<int>& coins, int amount) { 7 vector<int> dp(amount+1, 0); 8 //vector<int> dp(coins.size(), false); 9 dp[0] = 0; 10 for(int i=1; i<=amount; i++) { 11 dp[i] = INT_MAX; 12 for(int c:coins) { 13 if(i-c>=0 && dp[i-c]!=INT_MAX) { 14 dp[i] = min(dp[i],dp[i-c] + 1); 15 } 16 } 17 } 18 return (dp[amount] == INT_MAX)? -1:dp[amount]; 19 20 } 21 };
解法二:Greedy+DFS+Pruning (贪心算法+递归深度优先搜索+剪枝)
方针:将coins从大到小排序,每次尝试最大的面值,若总价刚好能够被coin整除,则将所用硬币个数->res
否则,从该面值的最大个数amount/coin开始依次递减,尝试:
剩下的余额amount-amount/coin*coin,用下一个小的面值硬币,需要的硬币个数。
Pruning:剪枝:
当
到目前为止的硬币个数cursum +
当前面值所需的硬币个数amount/coin + 1(由于未被整除,尝试剩余的小面额币种,至少要用一个硬币)
>= res
的时候,已经不可能是要求的最小结果,
因此可以直接return,不再尝试以后的
同样的余额下,用更小币种去替换,必然会用更多的硬币个数。
代码参考:
1 class Solution { 2 public: 3 void dfs(vector<int>& coins, int i, int amount, int cursum, int& res) { 4 if(i==coins.size()) return; 5 if(amount%coins[i]==0) { 6 res = min(res, cursum+amount/coins[i]); 7 return; 8 } 9 for(int cout = amount/coins[i]; cout >= 0; cout--) { 10 if(cout+cursum+1 >= res) return; //prunning 11 dfs(coins, i+1, amount-cout*coins[i], cursum+cout, res); 12 } 13 return; 14 } 15 int coinChange(vector<int>& coins, int amount) { 16 int res = INT_MAX; 17 sort(coins.rbegin(), coins.rend()); 18 dfs(coins, 0, amount, 0, res); 19 return (res == INT_MAX)? -1:res; 20 } 21 };