目录
1、初始化: 初始化一个dp数组做动态规划,记录凑i块钱要多少个硬币,pre数组,保存硬币组合,以及让f[0]=0(也就是凑0块钱要0个硬币)
2、状态更新:也就是判断前i-x块钱对应的最大硬币数+1个新的硬币能不能更新
最优硬币组合问题
问题描述
小C有多种不同面值的硬币,每种硬币的数量是无限的。他希望知道,如何使用最少数量的硬币,凑出给定的总金额N。小C对硬币的组合方式很感兴趣,但他更希望在满足总金额的同时,使用的硬币数量尽可能少。
例如:小C有三种硬币,面值分别为 1
, 2
, 5
。他需要凑出总金额为 18
。一种最优的方案是使用三个 5
面值的硬币,一个 2
面值的硬币和一个 1
面值的硬币,总共五个硬币。
测试样例
样例1:
输入:
coins = [1, 2, 5], amount = 18
输出:[5, 5, 5, 2, 1]
样例2:
输入:
coins = [1, 3, 4], amount = 6
输出:[3, 3]
样例3:
输入:
coins = [5], amount = 10
输出:[5, 5]
解题思路:
-
理解问题:
- 我们需要找到一种组合,使得使用最少数量的硬币凑出给定的总金额
amount
。 - 硬币的面值和数量是无限的。
- 我们需要找到一种组合,使得使用最少数量的硬币凑出给定的总金额
-
数据结构的选择:
- 我们可以使用一个数组
dp
,其中dp[i]
表示凑出金额i
所需的最少硬币数量。 dp[0]
初始化为0
,因为凑出金额0
不需要任何硬币。- 其他
dp[i]
初始化为一个较大的值(例如amount + 1
),表示初始状态下无法凑出该金额。
- 我们可以使用一个数组
-
算法步骤:
- 遍历每个金额
i
从1
到amount
。 - 对于每个金额
i
,遍历每种硬币面值coin
。 - 如果
coin
小于或等于i
,则更新dp[i]
为min(dp[i], dp[i - coin] + 1)
。 - 最终,
dp[amount]
就是凑出amount
所需的最少硬币数量。
- 遍历每个金额
-
记录硬币组合:
- 为了记录使用的硬币组合,我们可以在更新
dp[i]
时,同时记录使用的硬币面值。
- 为了记录使用的硬币组合,我们可以在更新
代码实现:
1、初始化: 初始化一个dp数组做动态规划,记录凑i块钱要多少个硬币,pre数组,保存硬币组合,以及让f[0]=0(也就是凑0块钱要0个硬币)
// 初始化 dp 数组,大小为 amount + 1,初始值为 10^9
vector<int> f(amount + 1, 1e9);
// 初始化 pre 数组,大小为 amount + 1,初始值为 0
vector<int> pre(amount + 1, 0);
// 初始条件:凑出金额 0 需要 0 个硬币
f[0] = 0;
2、状态更新:也就是判断前i-x块钱对应的最大硬币数+1个新的硬币能不能更新
例如:对于f[20]来说,假设f [15]为3(组合为5,5,5),此时,3+1也就是再加上一个5元硬币
for (int x : coins) {
// 遍历每个金额
for (int i = x; i <= amount; ++i) {
if (f[i - x] + 1 < f[i]) {
f[i] = f[i - x] + 1;
pre[i] = x;
}
}
}
3、保存硬币组合(要逆序):
这个没什么好说的了
// 反向追踪硬币组合
vector<int> ans;
int i = amount;
while (i > 0) {
ans.push_back(pre[i]);
i -= pre[i];
}
4、最后返回ans即可
最终代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> solution(vector<int> coins, int amount) {
// 初始化 dp 数组,大小为 amount + 1,初始值为 10^9
vector<int> f(amount + 1, 1e9);
// 初始化 pre 数组,大小为 amount + 1,初始值为 0
vector<int> pre(amount + 1, 0);
// 初始条件:凑出金额 0 需要 0 个硬币
f[0] = 0;
// 遍历每个硬币
for (int x : coins) {
// 遍历每个金额
for (int i = x; i <= amount; ++i) {
if (f[i - x] + 1 < f[i]) {
f[i] = f[i - x] + 1;
pre[i] = x;
}
}
}
// 反向追踪硬币组合
vector<int> ans;
int i = amount;
while (i > 0) {
ans.push_back(pre[i]);
i -= pre[i];
}
return ans;
}
int main() {
// 测试用例
vector<int> result1 = solution({1, 2, 5}, 18);
for (int num : result1) {
cout << num << " ";
}
cout << (result1 == vector<int>({5, 5, 5, 2, 1})) << endl;
vector<int> result2 = solution({1, 3, 4}, 6);
for (int num : result2) {
cout << num << " ";
}
cout << (result2 == vector<int>({3, 3})) << endl;
vector<int> result3 = solution({5}, 10);
for (int num : result3) {
cout << num << " ";
}
cout << (result3 == vector<int>({5, 5})) << endl;
return 0;
}
运行结果: