本文专注于记录我如何使用AI完成算法刷题练习,以此来分享如何高效利用AI刷题。
#123最优硬币组合问题
问题描述
现在有 M 种不同面值的硬币(硬币个数数目无限),如何用这些硬币组合为总数额为 N 的钱,同时满足使用硬币的个数最少?
输入格式
第一行为硬币面值正整数数组 (无序)
第二行为目标总数额,正整数
输出格式
总面值等于目标总数额的,且长度最小的硬币组合数组,结果不要求排序。如果不存在解则返回空数组
输入样例
[1,2,5]
18
输出样例
[5,5,5,2,1]
思路
这是一个典型的最小硬币数问题,可以采用动态规划来处理。我们依旧是明确输入,输出和处理。
输入
- 硬币面值数组:一个包含不同面值硬币的数组,每个面值都是正整数。例如
[1, 2, 5]。 - 目标总金额:一个正整数,表示需要用硬币组合成的总金额。例如
18。
输出
- 硬币组合数组:一个数组,表示组成目标总金额所需的最少硬币组合。如果无法组成目标总金额,则返回空数组
[]。例如[5, 5, 5, 2, 1]。
处理
设计一个算法能计算出所需最少硬币数,并且返回长度最小的硬币组合数组。
算法设计
采用动态规划来解决,核心思想是逐步计算出每个金额(从 0 到目标金额 N)可以用的最少硬币数,并存储对应的硬币组合。那么状态的定义和状态转移方程是一定要明确的。
-
dp[i]:- 表示组成金额
i所需的最少硬币数量。 - 初始化为
INT_MAX,表示默认不可达。 - 特例:
dp[0] = 0,因为金额为 0 时不需要任何硬币。
- 表示组成金额
-
状态转移方程:
dp[i] = min(dp[i], dp[i - coin] + 1)
代码实现
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
vector<int> solution(vector<int> coins, int amount) {
// dp[i] 表示组成金额 i 的最少硬币数
vector<int> dp(amount + 1, INT_MAX);
// last_coin[i] 用来追溯最小硬币组合
vector<int> last_coin(amount + 1, -1);
// base case, dp[0] = 0,0金额需要0个硬币
dp[0] = 0;
// 动态规划过程
for (int coin : coins) {
for (int i = coin; i <= amount; ++i) {
if (dp[i - coin] != INT_MAX && dp[i] > dp[i - coin] + 1) {
dp[i] = dp[i - coin] + 1;
last_coin[i] = coin;
}
}
}
// 如果 dp[amount] 仍然是 INT_MAX,说明无法组成 amount
if (dp[amount] == INT_MAX) {
return {};
}
// 通过 last_coin 数组恢复硬币组合
vector<int> result;
int current_amount = amount;
while (current_amount > 0) {
int coin = last_coin[current_amount];
result.push_back(coin);
current_amount -= coin;
}
return result;
}
int main() {
// 测试用例
vector<int> result = solution({1, 2, 5}, 18);
for (int num : result) {
cout << num << " ";
}
cout << endl; // 输出 5 5 5 2 1(顺序不固定)
return 0;
}