最少硬币问题--贪心算法

设有n种不同面值的硬币,各硬币的面值存于数组T〔1:n〕中。现要用这些面值的硬币来找钱。可以使用的各种面值的硬币个数存于数组Coins〔1:n〕中。
对任意钱数0≤m≤20001,设计一个用最少硬币找钱m的方法。
 

 用改进的贪心算法解决最少硬币问题,暂称之为贪心枚举法. 
由贪心算法可知尽量用大面值的硬币组合来找钱,可以使使用的硬币最少。而贪心算法对最少硬币问题的解决总是可以得到最优解很好的近似解。 
 本算法就是用贪心策略枚举出所有近似最优解,然后从中寻找到问题的最优解
寻找近似最优解群
例如:9分面值的硬币5枚,8分面值的硬币5枚,
 2分面值的硬币8枚,要找25分钱。
 设要找的钱为m,硬币种类为n,t[i](0<i<=n)为硬币的面值,c[i]为可以使用的各种面值的硬币个数, k[i]为第i种面值的硬币可以使用的最多个数
 (k[i]=min{m/t[i],c[i]})
(1)将硬币依面值大小排序
9 8 2 
(2)按面值种类划分不同情况
有多少种面值就划分多少种情况. 每种情况的第一枚硬币面值各不一样,其后对剩余的硬币按面值从大到小排列. 

划分为三个情况:982,892,298。
 对应k[i]为:k[0]=3, k[1]=3 ,k[2]=8
 得到近似最优解群为9分1枚,8分2枚;9分1枚,8分1枚,2分4枚;9分1枚,2分8枚.

算法优化
1,在寻找最优组合过程中,有些情况可以不予考虑。比如上例中2 9 8
2,在以小面值的硬币为第一个的情况中,在寻找最优组合时,会遇到两种情况:
 a、使用硬币个数要比以大面值的硬币(如9和8)为第一个的情况大得多。
 b、寻找到的组合与前面的情况有重复。

在程序中实现剪枝
 如果k[i]不比mincount大,则继续用贪心算法寻找近似最优组合。
如上例mincount初始值设为maxint。
 k[0]=3 < mincount, 则进行贪心选择,并将 最好结果放于mincount中mincount=3;
 k[1]=3 <= mincount=3, 进行贪心选择;
 k[2]=8 > mincount=3,则将其剪枝
这样可以有效的减小问题的规模。

该算法的优劣
1、对于硬币面值大的情况,执行效率会提高(因为k[i]变小了)。
2、对于硬币面值小的情况,问题规模会变的很大。
3、没有消除冗余。
 对于该算法还有很多可以值得继续探讨的地方
4.该算法的规模
(1)将硬币面值排序,O(nlogn)。
(2)寻找最优硬币组合
 n×∑k[i],若k= (∑k[i])/n,则O(kn*2)
该算法的规模为
 O(nlogn)+ O(kn*2)=O(kn*2)


这种算法仍然存在漏洞,因为使用了贪心选择.

完整程序代码如下:

#include <stdio.h>
#include <fstream.h>
#include<stdlib.h>
int n,money;
struct ctype
{
 int value;
 int coin;
};
template<class type>
void make2Darray(type * * &x,int rows,int cols)
{
 x=new type *[rows];
 for(int i=0;i<rows;i++)
 x[i]=new type[cols];
}
void swap(ctype &a,ctype &b)

ctype temp;
temp=a;
a=b;
b=temp;
}
int partition(ctype array[],int p,int r)
{
int i,j;
ctype key;
i=p;
j=r+1;
key=array[p];
while(true)
{
while(array[++i].value<key.value);
while(array[--j].value>key.value);
if(i>=j) break; 
swap(array[i],array[j]);
}
array[p]=array[j];
array[j]=key;
return j;
}
void quicksort(ctype array[],int p,int r) 
{
int q;
if(p<r)
{
q=partition(array,p,r);
quicksort(array,p,q-1);
quicksort(array,q+1,r);
}
}
void main()
{
 ifstream input("input.txt");
 ofstream output("output.txt");
 input>>n;
 int * coins=new int[n+1];
 ctype * T=new ctype[n+1];
 for(int i=1;i<=n;i++)
{
 input>>T[i].value;
 input>>T[i].coin;
}
 input>>money;
quicksort(T,1,n);
/*for(i=1;i<=n;i++)
{
coins[i]=T[i].coin;
}*/
 int max=0;
 for(i=1;i<=n;i++)  max+=T[i].coin;
 max+=10; 
 int * min=new int[money+1]; 
 min[0]=0;
int * * cnum;
 make2Darray(cnum,money+1,n+1);
 for(i=0;i<=money;i++)
 for(int j=1;j<=n;j++)
 cnum[i][j]=0;
if(T[1].value==1) {min[1]=1;cnum[1][1]=1;}
else min[1]=max;
 int j=2;
 while(j<=money)
{
 min[j]=max;
 i=1; 
 while((i<=n)&&(j>=T[i].value))
 {
   int coinumber=cnum[j-T[i].value][i];
 coinumber++;
 if((min[j]>1+min[j-T[i].value])&&(coinumber<=T[i].coin))
 { 
  for(int k=1;k<=n;k++) cnum[j][k]=cnum[j-T[i].value][k];
  cnum[j][i]++;
  min[j]=1+min[j-T[i].value];
 }
 i++; 
 }
   j++;
}
 if(min[money]!=max) output<<min[money];
 else output<<-1;
}
 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值