算法分析与设计实验:找零钱问题的贪心算法与动态规划解决方案

在计算机科学中,贪心算法和动态规划是两种常用的算法设计策略。本文将通过一个经典的找零钱问题,详细讲解这两种算法的实现和应用。我们将会提供完整的C++代码,并对代码进行详细解释,帮助读者更好地理解和掌握这两种算法。

问题描述

找零钱问题是这样一个问题:给定不同面值的零钱和一个总金额,如何使用最少数量的零钱来凑出这个总金额。例如,假设我们有面值为1、5、14、18的零钱,需要凑出28元,那么可能的解包括:28=18+5+5 或 28=14+14。

输入输出格式

输入:

  • 第一行:两个整数,分别表示零钱的种类数和需要凑的总金额。

  • 第二行:若干个整数,表示每种零钱的面值。

输出:

  • 分别输出贪心算法和动态规划算法得到的找零方案。

算法分析

贪心算法

贪心算法的核心思想是在每一步选择中都采取当前状态下最优的选择,从而希望导致最终结果最优。在找零钱问题中,贪心算法的策略是每次选择面值最大的零钱,尽可能多地使用这种零钱,直到无法再使用为止,然后继续选择次大的零钱,依此类推。

动态规划算法

动态规划算法通过把原问题分解为多个子问题来求解。对于找零钱问题,我们可以定义一个数组dp,其中dp[i]表示凑出金额i所需的最少零钱数量。通过填充电动态规划表,我们可以得到凑出总金额的最少零钱数,并通过回溯得到具体的找零方案。

代码实现

以下是使用C++实现的找零钱问题解决方案:

复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>

using namespace std;

vector<int> greedyCoinChange(int Y, vector<int> coins) {
    sort(coins.rbegin(), coins.rend()); // 降序排列
    vector<int> result;
    int remaining = Y;
    for (int coin : coins) {
        while (remaining >= coin) {
            result.push_back(coin);
            remaining -= coin;
            if (remaining == 0) break;
        }
        if (remaining == 0) break;
    }
    return result;
}

vector<int> dpCoinChange(int Y, vector<int>& coins) {
    vector<int> dp(Y + 1, INT_MAX); 
    vector<int> coinUsed(Y + 1, 0); 
    dp[0] = 0;

    for (int i = 1; i <= Y; ++i) {
        for (int coin : coins) {
            if (coin <= i && dp[i - coin] != INT_MAX && dp[i - coin] + 1 < dp[i]) {
                dp[i] = dp[i - coin] + 1;
                coinUsed[i] = coin;
            }
        }
    }

    vector<int> result;
    int current = Y;
    while (current > 0) {
        int coin = coinUsed[current];
        result.push_back(coin);
        current -= coin;
    }
    // 动态规划结果按降序排列
    sort(result.rbegin(), result.rend());
    return result;
}

int main() {
    int n, Y;
    cin >> n >> Y;
    vector<int> coins(n);
    for (int i = 0; i < n; ++i) {
        cin >> coins[i];
    }

    // 贪心算法直接处理降序
    vector<int> greedyResult = greedyCoinChange(Y, coins);

    // 动态规划处理后排序
    vector<int> dpResult = dpCoinChange(Y, coins);

    // 格式化输出
    cout << Y << "=";
    for (size_t i = 0; i < greedyResult.size(); ++i) {
        if (i != 0) cout << "+";
        cout << greedyResult[i];
    }
    cout << endl;

    cout << Y << "=";
    for (size_t i = 0; i < dpResult.size(); ++i) {
        if (i != 0) cout << "+";
        cout << dpResult[i];
    }
    cout << endl;

    return 0;
}

代码解析

贪心算法函数 greedyCoinChange

  1. 将零钱面值按降序排列,这样可以优先使用面值较大的零钱。

  2. 初始化一个空向量 result 来存储找零方案。

  3. 使用一个循环遍历每种面值的零钱,尽可能多地使用当前面值的零钱。

  4. 当剩余金额为零时,结束循环并返回找零方案。

动态规划函数 dpCoinChange

  1. 初始化一个动态规划数组 dp,其中 dp[i] 表示凑出金额 i 所需的最少零钱数量。

  2. 初始化一个数组 coinUsed 来记录凑出每个金额时使用的最后一个零钱面值。

  3. 使用嵌套循环填充电动态规划表,外层循环遍历金额,内层循环遍历零钱面值。

  4. 通过回溯 coinUsed 数组构造找零方案。

示例输入与输出

输入

复制代码
4 28
1 5 14 18

输出

复制代码
28=18+5+5
28=14+14

总结

本文通过一个具体的找零钱问题,详细介绍了贪心算法和动态规划算法的实现过程。贪心算法简单直观,但在某些情况下可能无法得到最优解。而动态规划算法虽然时间复杂度较高,但可以保证得到最优解。在实际应用中,我们可以根据问题的具体特点选择合适的算法。希望本文能够帮助读者更好地理解和掌握这两种重要的算法设计策略。

相关推荐
开心香辣派小星2 天前
23种设计模式-11代理模式
设计模式·代理模式
babywew13 天前
[2021一区Top刊复现+改进] Highlights:能源枢纽;多目标优化;碳排放管理;折中规划
代理模式
李子琪。3 天前
Metasploit渗透测试实战深度解析:从操作系统到应用服务的漏洞利用
经验分享·代理模式·课程设计
雨中飘荡的记忆4 天前
设计模式之代理模式详解
设计模式·代理模式
小二·4 天前
Spring框架入门:代理模式详解
java·spring·代理模式
繁华似锦respect4 天前
C++ 设计模式之代理模式详细介绍
linux·开发语言·c++·windows·设计模式·代理模式·visual studio
周杰伦fans10 天前
[特殊字符] 代理模式超详细讲解 ——.NET
数据库·c#·代理模式
phdsky11 天前
【设计模式】代理模式
设计模式·代理模式
司铭鸿11 天前
化学式解析的算法之美:从原子计数到栈的巧妙运用
linux·运维·服务器·算法·动态规划·代理模式·哈希算法
ZHE|张恒12 天前
设计模式(十二)代理模式 — 用代理控制访问,实现延迟加载、权限控制等功能
设计模式·代理模式