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分钱的最优解中面值ci 的数量。那么对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了。