AI刷题-最优硬币组合问题

目录

最优硬币组合问题

问题描述

测试样例

解题思路: 

 代码实现:

1、初始化: 初始化一个dp数组做动态规划,记录凑i块钱要多少个硬币,pre数组,保存硬币组合,以及让f[0]=0(也就是凑0块钱要0个硬币)

 2、状态更新:也就是判断前i-x块钱对应的最大硬币数+1个新的硬币能不能更新

3、保存硬币组合(要逆序): 

4、最后返回ans即可 

最终代码: 

运行结果: 


最优硬币组合问题

问题描述

小C有多种不同面值的硬币,每种硬币的数量是无限的。他希望知道,如何使用最少数量的硬币,凑出给定的总金额N。小C对硬币的组合方式很感兴趣,但他更希望在满足总金额的同时,使用的硬币数量尽可能少。

例如:小C有三种硬币,面值分别为 125。他需要凑出总金额为 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]

解题思路: 

  1. 理解问题

    • 我们需要找到一种组合,使得使用最少数量的硬币凑出给定的总金额 amount
    • 硬币的面值和数量是无限的。
  2. 数据结构的选择

    • 我们可以使用一个数组 dp,其中 dp[i] 表示凑出金额 i 所需的最少硬币数量。
    • dp[0] 初始化为 0,因为凑出金额 0 不需要任何硬币。
    • 其他 dp[i] 初始化为一个较大的值(例如 amount + 1),表示初始状态下无法凑出该金额。
  3. 算法步骤

    • 遍历每个金额 i 从 1 到 amount
    • 对于每个金额 i,遍历每种硬币面值 coin
    • 如果 coin 小于或等于 i,则更新 dp[i] 为 min(dp[i], dp[i - coin] + 1)
    • 最终,dp[amount] 就是凑出 amount 所需的最少硬币数量。
  4. 记录硬币组合

    • 为了记录使用的硬币组合,我们可以在更新 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;
}

运行结果: 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

像污秽一样

谢谢谢谢谢谢谢谢谢谢谢谢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值