贪心算法-找硬币

http://blog.csdn.net/cattycat/article/details/5813307

算法导论上第16-1问题

 

考虑用最少的硬币数找n分钱的问题,假设每个硬币的值都是整数。

 

先证明问题具有最优子结构。假设对找n分前有最优解,而且最优解中使用了面值c的硬币,最优解使用了k个硬币。那么,这个最优解包含了对于找n-c分钱的最优解。显然,n-c分钱中使用了k-1个硬币。如果n-c分钱还有一个解使用了比k-1少的硬币,那么使用这个解可以为找n分钱产生小于k个硬币的解。与假设矛盾。

 

对于有些情况下,贪心算法可能不能产生最优解。比如硬币面值1,10,25.找30分钱,最优解是3*10,而贪心的情况下产生的解是1*5+25.

 

问题b可以作为一个结论:假设可换的硬币单位是c的幂,也就是c0,c1, …, ck, 其中整数c>1, k>=1, 这种情况下贪心算法可以产生最优解。

问题的证明用到了引理:对于i=0,1,…, k, 设ai是找n分钱的最优解中面值c的数量。那么对i=0,1,…,k-1,有ai<c.

证明:假设ai>=c,对于0<=i<k. 则可以改进最优解通过增加一个面值ci+1的硬币和减少c个面值ci的硬币,这样找零钱的总数不变,但减少了c-1个硬币。于是,这个比最优解还要优化,矛盾。

 

然后证明问题具有贪心选择性质,即贪心选择具有最优解。这里证明不用贪心选择不能产生最优解。设j=max{0<=i<=k: ci<=n}.所以贪心解法会使用至少一个面值cj的硬币,考虑非贪心选择的解,不使用面值cj或更高的硬币。设非贪心的解使用ai数量的面值ci的硬币,对i=0,1, … , j-1. 所以有a0c0+a1c1+ … +aj-1cj-1=n.

因为n>=cj, 所以上式左边大于等于 cj. 现在假设这种非贪心选择的解是最优的,由引理ai<c,上式左边

a0c0+a1c1+ … +aj-1cj-1  <= (c-1)c0+(c-1)c1+ … +(c-1)cj-1 =(c-1)(1+c+…+cj-1)=(c-1)(cj-1)/(c-1)=(cj-1)<cj

与上面推出的左边大于等于cj矛盾。所以可以得到非贪心选择不能产生最优解。

贪心选择的话,问题的复杂度是O(k).K是硬币的个数。假设int a[k+1]是每个面值c0,c1, …, ck使用的数量。

for(int i=k;i>=0;i–)

{

   a[i]=n/ci;

   n%=ci;

}

所以如果问题是这种情况,可以直接用贪心而且问题的解确实是最优的。

 

问题d更一般,设计O(nk)时间的算法,能够对任意k种不同单位组合的硬币集合进行找换,假设其中一种硬币单位是一分。

上面已经说明问题具有最优子结构,可以用动态规划求解最优的找零钱的解。

假设c[j]是找j分钱最少的硬币数。硬币的面值是d1,d2,…,dk.由于存在一分钱,所以对j分钱总是存在可以找的零钱。

可以写出递推的式子:

cj=    0,  if j<=0

cj=    1+ min{c[j-di]}, 1<=i<=k  if j>1

 

写了一个完整的代码,并有输出找钱方案的。测试例子就是上面给出的反例。对25,10,1分钱,找30分钱。

#include <iostream>
using namespace std ;


#define N  64


void compute_change(int c[],int n ,int d[] ,int k,int a[]) 
{
	for(int j=1 ; j<=n ; j++)
	{
		c[j]=j;
		for(int i=1 ; i<=k ; i++)
		{
			if(j>=d[i] && (1+c[j-d[i]]) < c[j])
			{
				c[j]=1+c[j-d[i]] ;
				a[j]=d[i];
			}
		}
	}
}


void give_change(int a[],int j)
{
	if(j>0)
		cout<<"找零钱:"<<a[j]<<endl;
	give_change(a,j-a[j]);
}


int main()
{
	int d[]={0 , 25 , 10 ,  1} ;
	int c[N]={0} ;
	int a[N]={0};//记录找每个N分钱时最优的di值


	compute_change(c,30,d,3,a) ;
	cout << "最优解就是最少值为:" << c[30] << endl ;
	give_change(a,30);


}

===============================================

http://www.cnblogs.com/lgz24/archive/2010/06/15/1758698.html

贪心算法–找换硬币

找换硬币

       考虑用最少的硬币数来找N分钱的问题,假设每个硬币的值都是整数。

a)       请给出一个贪心算法,使得所换硬币包括一角的,五分的,二角五分的和一分的。证明所给出的算法能产生最优解。

b)      假设可换的硬币的单位是c的幂,也就是c^0,c^1,c^2,c^3……其中整数c>1,k>=1。证明贪心算法总可以产生一个最优解。

c)      请给出一组是贪心算法不能产生最优解的硬币单位集合。所给集合应当包括一分,以便保证对任意n值都有解。

d)      请给出一种O(nk)时间的算法,它能够对任意K种不同单位的硬币集合进行找换,假设其中一种硬币单位是一分的。

 

解:

a)       找换硬币的贪心算法思想:

(1)将找换集合中的元素进行降序排列,CurrentMax指向首元素;

(2)判断N是否大于找换集合中当前最大元素,如果大于,N=N-CurrentMax;再执行此步骤。如果不大于,则找换集合中的CurrentMax指向下一个元素,执行此步骤。

(3)若N=0,则找到了找换硬币的方法。

算法的源代码:

#include<stdio.h>

int main()

{

    freopen(“data.out”,”w”,stdout);

    int money,s1,s2,s3,s4,p,d,n,q,temp;

    scanf(“%d%d%d%d%d”,&money,&s1,&s2,&s3,&s4);

    temp=money;

    p = money/s1;   

       if (p >0)

        money -= p*s1;

    d = money/s2;

    if (d >0)

        money -= d*s2;

    n = money/s3;

    if (n >0)

        money -= n*s3;

q = money/s4;

 

    if (temp==(p*s1+d*s2+n*s3+q))

       {

         printf(“%d is sum %d\n”,s1,p);

         printf(“%d is sum %d\n”,s2,d);

         printf(“%d is sum %d\n”,s3,n);

         printf(“%d is sum %d\n”,s4,q);

       }

    else

        printf(“Not enough change\n”);

   return 0;

}

c) 找换集合的元素如果是{1,3,4},被找换的元素如果是10 ,那么采用上述方法得到的找换方法是:4,4,1,1。 而最优的方式是:4,3,3。

==================================

http://yixiong89921.blog.163.com/blog/static/132537788201137105357406/

贪心算法实现购物硬币找零问题  

硬币找钱问题

问题描述

设有6种不同面值的硬币,各硬币的面值分别为5分,1角,2角,5角,1元,2元。现要用这些面值的硬币来购物和找钱。购物时规定了可以使用的各种面值的硬币个数。

假定商店里各面值的硬币有足够多,顾客也可用多种方式支付。在1次购物中希望使用最少硬币个数。例如,1次购物需要付款0.55元,没有5角的硬币,只好用2*20+10+5共4枚硬币来付款。如果付出1元,找回4角5分,同样需要4枚硬币。但是如果付出1.05元(1枚1元和1枚5分),找回5角,只需要3枚硬币。这个方案用的硬币个数最少。

您的任务:对于给定的各种面值的硬币个数和付款金额,计算使用硬币个数最少的交易方案。

输入

有若干行测试数据。每一行有6个整数a5、a4、a3、a2、a1、a0和1个有2位小数的实数money,分别表示5分,1角,2角,5角,1元,2元面值的硬币个数和付款金额,money<=1000。文件以6个0结束(不必处理)。

输出

对每一行测试数据,一行输出最少硬币个数。如果不可能完成交易,则输出“impossible”。

输入样例

2 4 2 2 1 0 0.95

2 4 2 0 1 0 0.55

0 0 0 0 0 0

输出样例

2

3

 

 

具体代码稍后放出。

 4月12日更新代码,不好意思,CSDN的审核可能比较久……现在才出来http://download.csdn.net/source/3167555

其核心思想为消费者硬币数量有限,商店的硬币无限。因此问题可以用以下公式来描述

1、Min(消费者支付金币数量+商店找零金币数量);

2、支付值-找零值=商品值

寻找上面两个问题的最优解。

现在说说贪心算法的灵魂所在,下面会介绍为什么是这样。

这里所用的贪心算法为:MAX(消费者拥有的硬币面值-商店拥有的硬币面值)优先使用。

例如消费者拥有面值为2元的硬币,商店拥有5分的硬币,因此max=2元-5分=195分。那么上面所有的组合情况为

2元(不找零的情况),2元-5分,2元-1角,……,5分(不找零的情况)

现在假如我购买的商品是2元,那么用上面的组合中使用2元优先,只用1个硬币即可;假如商品是2元9角5分,我们从上面的序列中使用贪心算法来选择,那么就是一枚2元的硬币优先,那么9毛5如何处理呢,因为上面的序列中存在1元-5分存在的情况,这是能够使用的最大币值(贪心),就用它了,那么它是2枚硬币,即支付1元找零5分,总共用了3枚硬币。

接下来验证这个算法的贪心选择性和最优子结构性质,证明贪心算法可以获得最优解

贪心选择性:用贪心策略的选择替代原来的选择,如果能获得更优解或同优解,那么贪心策略就可以获得最优解。

我们把相应的序列列出来(用“分”来表示):

C[]={200,195,190,180,150,100,95,90,80,50,45,40,30,20,15,10,5};(除红色外其它面值都由两硬币之差构成)

证明这个序列具有贪心选择性质即可,这里我们先用一个简单的例子来证明

c[]={10,5,2,1},这里,c[i]的出现次数最多为{n,1,2,1},这样才能得到最优解,因为面值为1的次数为2时,可以用一个面值为2的来代替,硬币使用数就少了,那么就可以获得更优解。面值为2的硬币只能选用2个,若选用了3个,可以用1枚面值为5和一枚面值为1的硬币来代替。概括来讲,就是说如果c[i]超过一定数量,可以用更少的c[i-1]来代替,因此,我们优先选择大的面值的话,就会用更少的硬币数,因此满足贪心选择性质。

这里回归到我们问题的序列C[],容易看出C[i]总可以用更大的C[i-1]或者配合一些小币值来代替,但是数量<=C[i]的使用数量,因此满足贪心选择性质。

最优子结构性质证明:

设s[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是最少硬币数矛盾,故问题满足最优子结构性质。

伪代码这里就不写了,如果是软院的同学~还是自己认真写一下吧~我的代码在4月8号公布~也就是明天,给以后不懂的同学参考吧,基本上没什么bug了。

    原文作者:贪心算法
    原文地址: https://blog.csdn.net/han_jiang_xue/article/details/8476139
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞