代码改变世界

贪心法 货币支付问题或找零问题

2013-08-16 16:23  youxin  阅读(6187)  评论(0编辑  收藏  举报

 已知,中华人民共和国的纸币面额分别为:100元、50元、20元、10元、5元、2元、1元,输入钱数,输出最小的货币方案。

 

#include<iostream>
using namespace std;
int denomination[7]={100,50,20,10,5,2,1};

int  giveChange(int money,int (*result)[2])
{
     
    int k=0;
    int sum=0;
    for(int i=0;i<7;i++)
    {
        if(money==0) break;
        if(money/denomination[i] > 0)
        {
            sum+=money/denomination[i] * denomination[i];
            result[k][0]=denomination[i];
            result[k][1]=money/denomination[i];
            money=money-sum;
            k++;
            
        }
     
    }
    return k;
}

int main()
{
    int n; //钱总数
    
    while(cin>>n)
    {
     
        int result[100][2];
        int k=giveChange(n,result);
        cout<<"需要"<<k<<""<<endl;
        for(int i=0;i<k;i++)
        {
            cout<<result[i][0]<<"面币"<<result[i][1]<<""<<endl;
        }
        cout<<endl;
    }
}

 

或:

//find_change.cpp
/*
找零钱问题:
    以人民币1元,2元,5元,10元,20元,50元,100元为例,要求所找的张数最少.
贪心解法:
    要找N分钱,先拿N除最大零钱面值,取整就是所找的最大面值零钱的个数,取模得
    出余数重新赋给N.重复操作直到除最小面值结束. 
*/
#include<iostream>
using namespace std;
int main()
{
    int n, i;
    const int parValue[7] = {100,50,20,10,5,2,1};//面值从大到小排列
    int cnt, anNumber[7];//找钱方案,各币值数量
    while(cin>>n && n) {
        memset(anNumber, 0, 7*sizeof(int));
        
        i = 0, cnt = 0;
        while (n > 0) {
            anNumber[i] = n/parValue[i];
            n = n%parValue[i];
            cnt += anNumber[i];
            ++i;
        }
        
        //输出
        cout<<"面 值:100 50 20 10 5   2   1"<<endl;
        cout<<"找零数:";
        for (i = 0; i < 7; ++i) {
            printf("%-3d ", anNumber[i]);
        }
        cout<<endl;
        cout<<"需找钱张数:"<<cnt<<endl;
    }
    return 0;
}

 

参考了:

http://blog.csdn.net/cxxsoft/article/details/935835

贪心算法在这种方法在这里之所以总是最优,是因为银行对其发行的硬币种类和硬币面值的巧妙安排。
 如果只有面值分别为1,5和11单位的硬币,而希望找回总额为15单位的硬币,按贪婪算法,应找1个11单位面值的硬币和4个1单位面值的硬币,共找回5个硬币。但最优的解答应是3个5单位面值的硬币。

证明人民币找零问题贪心算法的正确性

问题提出:

根据人们生活常识,我们到商店里买东西需要找零钱时,收银员总是先给我们最大面值的,要是不够再找面值小一点的,直到找完为止。这就是一个典型的贪心选择问题。 问题描述:

当前有面值分别为100 元、50 元、20 元、10 元、5元、1元, 5角, 2角、1角的人民币。证明人民币在找零时(1-99元)符合贪心算法,即证明此问题满足贪心算法的两个基本要素:即最优子结构性质和贪心选择性质。

问题证明:

当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。在人民币找零问题中,其最优子结构性质表现为:

设c[i]是各面额人民币使用的数量,S[i]是商品价格为n时的最优解,数量为K。现在设某面值的人民币数量减一:S[j]=S[j]-1,则新的S[i]为n-c[j]的最优解,纸币数K-1. 否则,设T[i]是n-c[j]的最优解,纸币数为m,即m<k-1.那么对于n来说,T[i]+1应该为原问题最少纸币数,即m+1<k-1+1=k,此与k为最少纸币数矛盾,故问题满足最优子结构性质。 贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。在人民币找零问题中,满足贪心选择性质的证明如下:

设纸币面额100,50,20,10,5,2,1元的数量依次为A,B,C,D,E,F,G,则根据贪心算法思想得到的解应依次保证max(A),max(B),max(C),max(D),max(E),max(F),max(G)。假设存在更优的算法,使得所用的纸币数更少,即数量至少小于或等于A+B+C+D+E+F+G-1。那么在纸币总数减少的情况下保证总额不变只能增大相对大面额纸币的数量并减少小面额纸币数量。而由贪心算法知max(A)已经是最大的了,以此类推,max(B),max(C),max(D),max(E),max(F)均应为最大数量了,所以贪心算法得到的解是最优解,即满足贪心选择性质。

综上所述,人民币找零问题满足贪心算法。

 

帖子:请教,一个问题满足什么样的条件才能保证贪心算法可以得出最优解?
在看书的时候遇到一个“找零钱”问题:目标是找67分,面值有25、10、5、2、1,目标是所用硬币数量最少。此问题用贪心可得最优解,即每次拿出不会导致总面值超过目标面值的最大面值:25+25+10+5+2=67。
但是,如果换成目标面值10分,面值种类有7、5、1,那么贪心法就不好用了,因为7+1+1+1=5+5。
想到这里,就想请教大家,一个问题满足什么样的条件才能保证贪心算法可以得出最优解?
我想应该有比较简洁的结论吧。
谢谢。

参考:http://bbs.csdn.net/topics/330085072