322. Coin Change

问题:

给定一组硬币面值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 };

 

posted @ 2020-08-26 19:33  habibah_chang  阅读(182)  评论(0编辑  收藏  举报