1、Lintcode 669. 换硬币
(1)题目描述
给出不同面额的硬币以及一个总金额。写一个方法来计算给出的总金额可以换取的最少的硬币数量。如果已有硬币的任意组合均无法与总金额面额相等,那么返回 -1。
你可以假设每种硬币均有无数个。
样例1
输入:
[1, 2, 5]
11
输出: 3
解释: 11 = 5 + 5 + 1
样例2
输入:
[2]
3
输出: -1
(2)题解-敲代码
每种硬币数量不限,可以看作完全背包。需要装满背包,是恰好背包。—— 完全恰好背包。
一维完全恰好背包:(1) 初始化方式; (2)二层循环正序; (3)状态转移方程按01处理; (4)检查是否恰好装满;
状态定义:
dp[ i ][ j ] —— 考虑到第i个物品时,所需要的最少硬币数。
完全背包-状态转移方程:
dp[j] = min(dp[j], dp[j-k*coins[i]] + k); (k*coins[i] <= j)
01背包-状态转移方程:
dp[j] = min(dp[j], dp[j-coins[i]] + 1); // 公式按01背包处理
代码一:
class Solution {
public:
/**
* @param coins: a list of integer
* @param amount: a total amount of money amount
* @return: the fewest number of coins that you need to make up
*/
int coinChange(vector<int> &coins, int amount) {
// write your code here
if(amount == 0) return 0;
int n = coins.size();
if(n==0 && amount) return -1;
// 一维dp,完全恰好背包
vector<int> dp(amount+1, INT_MAX); // 没有硬币,其他总金额所需最少硬币数为无穷大
dp[0]=0; // 恰好背包,总金额0不需要硬币
for(int i=0; i<n; i++) // 硬币
{
for(int j=coins[i]; j<=amount; j++) // 当前总金额,正序,完全背包解法,里面是01背包dp公式
{
if(dp[j-coins[i]] == INT_MAX) continue; // 无穷大会溢出
dp[j] = min(dp[j], dp[j-coins[i]] + 1); // 公式按01背包处理
}
}
if(dp[amount] == INT_MAX) // 是否恰好装满
return -1;
else
return dp[amount];
}
};
代码二:将无穷大定义为 INT_MAX/2,这样可以继续加数,一般不会溢出
class Solution {
public:
/**
* @param coins: a list of integer
* @param amount: a total amount of money amount
* @return: the fewest number of coins that you need to make up
*/
int coinChange(vector<int> &coins, int amount) {
// write your code here
if(amount == 0) return 0;
int n = coins.size();
if(n==0 && amount) return -1;
// 一维dp,完全恰好背包
vector<int> dp(amount+1, INT_MAX/2); // 没有硬币,其他总金额所需最少硬币数为无穷大
dp[0]=0; // 恰好背包,总金额0不需要硬币
for(int i=0; i<n; i++) // 硬币
{
for(int j=coins[i]; j<=amount; j++) // 当前总金额,正序,完全背包解法,里面是01背包dp公式
{
//if(dp[j-coins[i]] == INT_MAX/2) continue; // 不考虑无穷大会溢出
dp[j] = min(dp[j], dp[j-coins[i]] + 1); // 公式按01背包处理
}
}
if(dp[amount] == INT_MAX/2) // 是否恰好装满
return -1;
else
return dp[amount];
}
};