# [322. Coin Change](https://leetcode.com/problems/coin-change/)

# 思路
换零钱，给定一个钱的价值amount和一些面值，假设每个面值的硬币数都是无限的，问我们最少能用几个硬币组成给定的价值。

## 思路一
此题很容易想到直接搜索所有可能然后保持一个全局的结果res，但是直接搜索很容易超时。因此需要做一些剪枝，为了剪枝剪得更狠，我们可以先对面值数组coins从大到小
排个序，这样做的原因是如果某个amount可以直接被某个面值整除，那就可以直接返回整除的结果而不用考虑比这个面值小的面值了。

我们定义一个搜索函数helper，传入排好序的coins、面值开始索引start（初始为0）、当前目标target（初始为amount）、当前已用硬币数（初始为0）、全局结果res（初始为INT_mAX，引用传入）:
* 当start超出coins数组的范围，直接返回即可；
* 当target可以被 coins[start] 整除，那么全局res就更新为`min(res, cur + target / coins[start])`然后返回。
* 否则就需要进行递归搜索了，此时我们需要采取贪心的策略，即从最多可加入硬币coins[start]的数目`target / coins[start]`开始逐渐递减（代码中这个次数为i），
一旦遇到`cur + i >= res - 1`直接break，因为当前的i递归下去最少需要的硬币数`cur + i + 1`不会比res小，所以就不用进行搜索了；而再循环下去（即i--）res也不可能比当前res小（想想为什么），因此也不用再循环下去了。
这个剪枝很关键，少了这个剪枝就会超时，加上这个剪枝直接击败99%。若不满足这个剪枝条件，那么递归下去就是。

## 思路二
熟悉动态规划的童鞋一眼就可以看出这其实就是个背包问题，具体来说是一个恰好装满的完全背包问题，
我在我的博客文章[动态规划之背包问题系列](https://tangshusen.me/2019/11/24/knapsack-problem/)中对常见的几类背包问题做了个总结，此题的分析见5.2节，这里只给出代码。



# 思路
## 思路一
``` C++
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        int res = INT_MAX, n = coins.size();
        sort(coins.begin(), coins.end(), greater<int>());
        helper(coins, 0, amount, 0, res);
        return (res == INT_MAX) ? -1 : res;
    }
    void helper(vector<int>& coins, int start, int target, int cur, int& res) {
        if (start >= coins.size()) return;
        if (target % coins[start] == 0){
            // 能整除的话肯定不用再递归了, 因为coins已从大到小排好序
            res = min(res, cur + target / coins[start]);
            return;
        }
        for (int i = target / coins[start]; i >= 0; --i) {
            // 满足这个条件就不用搜索也不用循环了
            if (cur + i >= res - 1) break;
            helper(coins, start + 1, target - i * coins[start], cur + i, res);
        }
    }
};
```

## 思路二
``` C++
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int>dp(amount + 1, INT_MAX);
        dp[0] = 0;
        
        for(int i = 1; i <= coins.size(); i++)
            for(int j = coins[i-1]; j <= amount; j++){
                // 下行代码会在 1+INT_MAX 时溢出
                // dp[j] = min(dp[j], 1 + dp[j - coins[i-1]]);
                if(dp[j] - 1 > dp[j - coins[i-1]])
                    dp[j] = 1 + dp[j - coins[i-1]];   
            }
        
        return dp[amount] == INT_MAX ? -1 : dp[amount];
            
    }
};
```
