• 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 };
  • 相关阅读:
    Java反射(2)访问字段(Field)
    《编程珠玑》中“位图排序”引发的一系列实验
    Java : 传值or传引用?
    const 指针
    三种数据库访问——Spring3.2 + Hibernate4.2
    三种数据库访问——Spring JDBC
    数据库连接池:Druid
    三种数据库访问——原生JDBC
    介绍4款json的java类库 及 其性能测试
    云存储(Swift+Keystone)部署策略
  • 原文地址:https://www.cnblogs.com/habibah-chang/p/13567195.html
Copyright © 2020-2023  润新知