找零问题(Java实现)——贪心算法

贪心算法也称为贪婪算法。

下述找零问题应用的方法就是贪心算法。现在思考,有没有更好的找零办法,使找出的硬币个数更少呢?也就是说,对于找零问题的这个实例,这个解是不是最优的呢?实际上,可以证明,就这些面额的硬币来说,对于所有正整数的找零金额,贪心算法都会输出一个最优解。

贪心算法在解决问题的策略上目光短浅,只根据当前已有的信息就做出认为是最好的选择,而且一旦做出了选择,不管将来有什么结果,这个选择都不会改变。换言之,贪心算法并不是从整体最优考虑,它所做出的选择只是在某种意义上的局部最优。

一、问题描述       

        来看一个找零钱的例子,在我国广泛使用的硬币的面额是 1元、5角、1角、5分、2分、1分(当然现在没有分了)。假如某顾客买完东西,现在要找给他 9分钱,要求找出的硬币个数最少,那么如何用这些面额的硬币找零呢?这里,采用的方法是:首先选出一个面值不超过 9 分的最大硬币,即 5 分钱硬币;从 9 分中减去 5 分,剩下 4 分,再选出一个面值不超过 4 分的最大硬币,即 2 分硬币;从 4 分中减去 2 分,剩下 2 分,最后选出一个面值不超过 2 分的最大硬币,即 2 分硬币。

用一个公式即可表示为 9 = 5+2+2,总计找出的硬币个数为 3 。

二、贪心算法的求解过程

        贪心算法的典型应用是求解最优化问题,算法往往从一个初始状态出发,来构造问题的解,以满足约束方程为条件,运用贪心策略不断的扩充解集合,直至得到问题的解。

      用贪心法求解问题应该考虑以下几个方面。

      (1)候选集合C:为了构造问题的解决方案,有一个候选集合C作为问题的可能解,即问题的最终解均取自于候选集合C。例如,在找零问题中,各种面值的硬币构成候选集合。

      (2)解集合S:初始时为空,随着贪心选择的进行,解集合S不断扩展,直到构成一个满足问题的完整解。例如,在找零问题中,已找出的硬币构成解集合。

      (3)解决函数solution: 检在解集合S是否构成问题的完整解。例如,在找零问题中,解决函数是已找出的硬币的总金额应恰好等于应找额。

      (4)选择函数select: 即贪心策略,这是贪心算法的关键,它指出哪个候选对象最有希望构成问题的解,选择函数通常和目标函数有关。例如,在找零问题中,贪心策略就是在候选集合中选择面值最大的硬币。

      (5)可行函数feasible:检查在原解集合中加人一个候选对象后是否可行,即解集合扩展后是否满足约束条件。例如,在找零问题中,可行函数是每步选择的硬币 和已找出 的硬币相加不超过应找额。

三、程序源代码及运行截图

package SF;

public class 找零钱问题 {
	int i;
	static int n = 5;
	double m = 0.09;
	double m1 = 0.09;
	//主函数
	public static void main(String[] args) {
		double[] V = new double[]{1,0.5,0.1,0.05,0.02,0.01};
		int[] X = new int[6];
		找零钱问题 p = new 找零钱问题();
		X=p.Greedy(V);
		int s = X[0]+X[1]+X[2]+X[3]+X[4]+X[5];
		System.out.println("得到找零钱问题的最优解为:X = { "+X[0]+" "+X[1]+" "+X[2]+" "+X[3]+" "+X[4]+" "+X[5]+" }共需要最少"+s+"枚硬币。");
	}
	//贪心算法-找零问题
	int[] Greedy(double C[]){
		int i=0;
		double x = 0;
		double[] S = new double[n+1];
		int[] Z = new int[n+1];
		for(i=0;i<=n;i++){
			if( solution(S)==0 ){
				x = select(C);
				if (feasible(S,x)==1)
				{
					S[i] = x;
					Z[this.i]++;
					m1 = ((double)((int)(m1*100) - (int)(x*100)))/100;
				}
			}
		}System.out.println("使用硬币集为:{ "+S[0]+"  "+S[1]+"  "+S[2]+" }");
		return Z;
	}
	//解决函数 solution
	int solution(double X[]){
		double s = 0;
		for(int i=0;i<=n;i++){
			s=((double)(int)(s*100)+(int)(X[i]*100))/100;
		}
		if(s==m)
			return 1;
		else
			return 0;
	}
	//选择函数 select
	double select(double X[]){
		int i = 0;
		double x = 0;
		do{X[i]=0;i++;}
		while(X[i]>m1||X[i]==0);
		this.i = i;
		x = X[i];
		return x;
	}
	//可行函数 feasible
	double feasible(double X[],double x){
		int i;
		int j=1;
		double y=0;
		for(i=0;i<=n;i++){
			y=y+X[i];
		}
		y=y+x;
		return j;
	}
}

运行截图:

  • 7
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值