题目描述:
You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1
.
Example 1:
coins = [1, 2, 5]
, amount = 11
return 3
(11 = 5 + 5 + 1)
Example 2:
coins = [2]
, amount = 3
return -1
.
Note:
You may assume that you have an infinite number of each kind of coin.
程序代码:
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
// dp[j]表示总价值为j时,最少的硬币数量
// dp[0]初始化为0,其余都初始化为最大整数值
vector<int> dp(amount + 1, INT_MAX);
dp[0] = 0;
// 类似可重复背包,但每次比较取较小值
// 注意需要判断dp[j-coins[i]] != INT_MAX,否则可能溢出
for (int i = 0; i < coins.size(); i++) {
for (int j = coins[i]; j <= amount; j++) {
if (dp[j - coins[i]] != INT_MAX) {
dp[j] = min(dp[j], dp[j - coins[i]] + 1);
}
}
}
// 若dp[amount] >= INT_MAX, 说明无法组合取得总价值为amount
if (dp[amount] >= INT_MAX)
return -1;
else return dp[amount];
}
};
简要题解:
本题是一道有关动态规划的问题。
先理清题意。本题输入是一组硬币(这组硬币中每个面额都可以无限使用)和一个amount值,要求输出硬币总面额为amount值的最小硬币数量,若无法达到amount值,则输出-1.
一开始,我认为本题应该类似一般的背包问题,只不过有以下几点不同:初始化时除了dp[0]初始化为0以外,其他都初始化为INT_MAX(这是因为后面要比较取min值);需要将背包问题中每次取两个价值中的max值改为取min值;且需要将每个硬币的价值都看作1.
但仔细分析后发现,除了以上几点不同点之外,本题实际上还是一个可重复背包问题,因为每个面额的硬币都可以无限次取用。
分析过后,就可以列出转移方程了。类似于可重复背包问题,可以列出转移方程为:
先初始化dp[0] = 0. 其他dp中的值为INT_MAX
外层循环枚举i = 0...到coins.size(),内层循环枚举j = coins[i] ...到amount (这里就是可重复背包与普通背包的主要不同之处,若是普通背包,则是从amount到coins[i]):
若dp[j - coins[i] ]不为INT_MAX(防止计算溢出),则dp[j] = min{dp[j], dp[j - coins[i]] + 1} (表示从 j 的总面额中去掉一个coins[i]的面额,并相应加上价值1)
最后输出时,只需判断dp[amount]是否>= INT_MAX, 若是,则说明计算取min的过程中其没有取过dp[j - coins[i]] + 1,也就是说其没有取出过某个硬币coins[i]的面额,那么amount的总面额无法被硬币组合得到,则输出为-1; 若不是,则输出即为dp[amount].
本题巧妙地改造了可重复背包问题,是一道比较有意思的动态规划问题。