题目描述
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
思路
代码
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Solution {////返回可以凑成总金额所需的 最少的硬币个数
public:
int coinChange(vector<int>& coins, int amount) {
int n = coins.size();
// 二维数组:状态定义:dp[i][j]表示从前 i 个物品中选择不超过 j 重量的物品的最大价值
vector<vector<int>> dp(n + 1, vector<int>(amount + 1, amount + 1));
dp[0][0] = 0;
// Tips:前 i 个物品表示的是编号从 [ 0 , i - 1] 这些物品,即最后一个物品编号为 i - 1
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= amount; j++) {
// 背包容量小于当前物品的容量,即背包容量已经不足以拿第 i 个物品了
// 第 i 个物品即下标为 i - 1 的那个物品
if (j < coins[i - 1]) {
dp[i][j] = dp[i - 1][j];
}
// 背包容量足够拿第 i 个物品,那么可拿也可不拿
// 1、拿了,那么最大价值是前 i 个物品扣除第 i 个物品的重量的情况下的最大价值,再加上【第 i 个】物品的价值
// 2、不拿,那么就是从前 i - 1 个物品中选择出的最大价值
else {
dp[i][j] = min(dp[i - 1][j], dp[i][j - coins[i - 1]] + 1);
}
}
}
return dp[n][amount] == amount + 1 ? -1 : dp[n][amount];//如果没更新,即找不到组合,就输出-1
}
};
class Solution2 {////返回可以凑成总金额所需的 最少的硬币个数
public:
int coinChange(vector<int>& coins, int amount) {
int Max = amount + 1;
vector<int> dp(amount + 1, Max);
dp[0] = 0;
for (int i = 1; i <= amount; ++i) {
for (int j = 0; j < (int)coins.size(); ++j) {
if (coins[j] <= i) {
dp[i] = min(dp[i], dp[i - coins[j]] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
};
vector<int> split(string params_str) {
vector<int> p;
while (params_str.find(",") != string::npos) {
int found = params_str.find(",");
p.push_back(stoi(params_str.substr(0, found)));
params_str = params_str.substr(found + 1);
}
p.push_back(stoi(params_str));
return p;
}
int main() {
string input_str;
cin >> input_str;//coins = [1,2,5]
vector<int> coins;
coins = split(input_str.substr(1, input_str.size() - 2));
int amount;
cin >> amount;//amount = 11
Solution a;
cout << a.coinChange(coins, amount) << endl;
}
变型题
题目要求:
返回可以凑成总金额的硬币组合数
代码
class Solution {
public:
int change(int amount, vector<int>& coins) {
// 经典的完全背包问题
// 二维数组:状态定义:dp[i][j]表示从数组 coins 中前 i 个硬币中选择得到价值为 j 的硬币组合数
int n = coins.size();
vector<vector<int>>dp(n + 1, vector<int>(amount + 1 , 0));
// 只有当不选取任何硬币时,金额之和才为 0,此时只有 1 种硬币组合
dp[0][0] = 1;
// Tips:前 i 个物品表示的是编号从 [ 0 , i - 1] 这些物品,即最后一个物品编号为 i - 1
// coins 数组的大小就是硬币的个数
for( int i = 1 ; i <= n ; i++){
// 遍历背包容量
for( int j = 0 ; j <= amount ; j++){
// 背包容量小于当前硬币的值,即背包容量已经不足以拿第 i 个硬币了
// 第 i 个硬币即下标为 i - 1 的那个硬币
if( j < coins[i - 1]){
// 硬币组合数就是去考虑当前背包容量情况下,从前 i - 1 个物品中选择出的硬币组合数
dp[i][j] = dp[i - 1][j];
// 背包容量足够拿第 i 个物品,那么可拿也可不拿
// 1、拿了,那么硬币组合数是前 i 个硬币扣除第 i 个硬币的情况下的硬币组合数
// 2、不拿,那么就是从前 i - 1 个硬币中选择出的硬币组合数
}else{
dp[i][j] = dp[i-1][j] + dp[i][j-coins[i-1]];
}
}
}
return dp[n][amount];
}
};