zoukankan      html  css  js  c++  java
  • 最少钱币数-1-贪心算法(错在哪里)-编程练习题(50)

    目录

    题目:

    分析:

    贪心算法C++代码(有问题):

    总结:


    自己保存的编程练习题,可以看这里CG-OJ系统编程练习题

    题目:

    最少钱币数
    问题描述
    这是一个古老而又经典的问题。用给定的几种钱币凑成某个钱数,一般而言有多种方式。例如:给定了 6 种钱币面值为 2、5、10、20、50、100,用来凑 15 元,可以用 5 个 2 元、1个 5 元,或者 3 个 5 元,或者 1 个 5 元、1个 10 元,等等。显然,最少需要 2 个钱币才能凑成 15 元。
            你的任务就是,给定若干个互不相同的钱币面值,编程计算,最少需要多少个钱币才能凑成某个给出的钱数。
    输入形式 输入可以有多个测试用例。每个测试用例的第一行是待凑的钱数值 M(1 <= M<= 2000,整数),接着的一行中,第一个整数 K(1 <= K <= 10)表示币种个数,随后是 K个互不相同的钱币面值 Ki(1 <= Ki <= 1000)。输入 M=0 时结束。
    输出形式 每个测试用例输出一行,即凑成钱数值 M 最少需要的钱币个数。如果凑钱失败,输出“Impossible”。你可以假设,每种待凑钱币的数量是无限多的。
    样例输入 15
    6 2 5 10 20 50 100
    1
    1 2
    0
    样例输出 2
    Impossible

    分析:

    要想钱币数目最少,肯定紧着最大面值的钱用,当前剩余的钱数小于最大面值的钱的话就用第二大面值,以此类推,直到待凑的钱数值为0,或者不为0(则输出Impossible)。基于这种想法,算法上叫贪心算法,我们还可以每次都算一次Money除以maxCoins最大面值的钱数值,得到用几张最大钱币,余数即为剩余钱币数,这样我们只用循环钱币种数k次就能得出答案。

    minCoin += money/coins[i]; //钱币数
    money = money%coins[i];    //剩余钱数

    然而并没有那么简单,考虑这样一组数据,例如有 1元,7元,9元,10元四种钱币的情况下,要凑18元 ,贪心算法会给出答案需要3张(10元1张,7元1张,1元1张)但是我们可以明显看出需要 2张(9元两张)足矣,显然贪心算法算出来的是错误的。那么怎么办呢,只有用动态规划算法了。详见:最少钱币数(凑硬币)详解-2-动态规划算法(初窥)-CCF-CSP练习题。为什么贪心算法有问题还贴出代码?仅供自己以后回忆参考,加深印象。

    贪心算法C++代码(有问题):

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int main()
    {
        bool cmp(int a,int b);//降序排序比较函数
        int coins[10];//硬币数目,由于题目给出不超过10种,所以我申请了10。
        int money; /*待凑钱的数值*/
        int kind;  /*钱币种类数目*/
        int minCoin =0 ;//最少钱币数
        int i;
        while(1)
        {
            minCoin = 0;//每次初始化为0
            cin >> money;     //读入待凑钱数
            if(0 == money) break; //如果为0,则退出。
            cin >> kind;          //读入拥有钱币种类数
            for(i=0; i<kind; i++)
            {
                cin >> coins[i];  //依次读入钱币种类
            }
            sort(coins,coins+kind,cmp);  //降序排序钱币种类
            for(i=0; i<kind; i++)
            {
                if(money-coins[i] >= 0)
                {
                    minCoin += money/coins[i];
                    money = money%coins[i];
                }
            }
            if( 0 == money )
            {
                cout << minCoin << endl;
            }else{
                cout << "Impossible" <<endl;
            }
    
        }
        return 0;
    }
    
    bool cmp(int a,int b) /* 降序排序比较函数,当a>b时,不交换;当a<b时交换*/
    {
        return a > b;
    }
    

    总结:

    这个解法在oj系统中有问题,只得了50分,剩下的每组数据有1个是错误的。所以在CCF-CSP考试时如果不会动态规划的话可以把贪心算法写上去,能骗点分数。

    使用贪心算法解决凑硬币问题时,有些情况下是可以得出正解的,比如后一个钱币面值没有达到前一个钱币面值的2倍时。对于有的问题得出的解是错误的。比如有3种面值分别为3元,5元,7元的纸币,(1)那么至少用几张纸币能凑够10元?我的直觉告诉我先选面值最大的,7元一张,然后再选面值5元的时候发现超额了(7+5>10),因此我们选3元一张,最少用2张纸币就能凑够10元,这个都能想出来的方法可以叫贪心算法(每次都选当前看来最好的选择,不从整体最优考虑),(2)那么至少用几张纸币能凑够8元呢?如果还按照贪心算法来解的话会得到Impossible。因为先选一张7元,然后再选5元(7+5>8)不行,换选3元(7+3>8)还不行。但是仔细看会发现5元+3元不是8元吗,怎么会无解。所以贪心算法解这个问题是不行的。CSP考试时只能用来骗点分。

    到这里我们发现用贪心算法会出现两个问题 1)本来有解用贪心法算出来却无解,例如上例(2)至少用几张纸币能凑够8元?;2)算出来的解不是最优解,例如有 1元,7元,9元,10元四种面值的纸币,要凑18元 ,贪心算法会给出答案需要3张(10元1张,7元1张,1元1张),但是我们可以明显看出 2张(两张9元)才是最优解。

    那么问题出在了哪里?在这里用贪心算法是有条件的——后一个的权值(这里就是纸币面值)是前一个的2倍或以上才可以使用,这里10不到9的两倍。贪心算法不是对所有问题都能得到整体最优解。关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。在这里我们先选择了10元,在第二步的时候9元就选择不了了(10+9>18了),选择10元这个状态影响到选9元这个状态,所以会错过9+9这个最优解。所以说贪心算法在对问题求解时,总是做出在当前看来是最好的选择,不从整体最优考虑。

  • 相关阅读:
    Linux 命令[5]:rmdir
    Linux 命令[4]:pwd,date
    Linux 命令[0]:起航
    Linux 命令[3]:cd
    vscode插件
    Object.freeze()
    插件
    前端开发调试线上代码
    前端自动化测试是浪费时间还是节约时间?
    踩坑之用lrz插件进行图片压缩
  • 原文地址:https://www.cnblogs.com/www-helloworld-com/p/10202961.html
Copyright © 2011-2022 走看看