数据结构与算法(6)贪心法

贪心法(greedymethod)是把一个复杂问题分解为一系列较为简单的局部最优选择,每一步选择都是对当前解的一个扩展,直到获得问题的完整解。

正如其名字一样,贪心法在解决问题的策略上目光短浅,只根据当前已有的信息做出选择,而且一旦做出了选择,不管将来有什么结果,这个选择都不会改变。


例如用贪心法求解付款问题:假设有面值为{5元、2元、1元、5角、2角、1角}的货币若干张,需要付款4元6角现金,如何付款才能使付出货币的数量最少?

款问题的贪心选择策略是,在不超过应付款金额的条件下,选择面值最大的货币。
首先选出1张面值不超过4元6角的最大面值的货币,即2元;
再选出1张面值不超过2元6角的最大面值的货币,即2元;
再选出1张面值不超过6角的最大面值的货币,即5角;
再选出1张面值不超过1角的最大面值的货币,即1角。
总共付出4张货币。


显然,贪心法的关键是设计合理的贪心选择策略。在本书中,哈夫曼算法、Prim算法、Kruskal算法、Dijkstra算法等都是贪心法的应用实例。

算法设计实例——埃及分数

埃及同中国一样,也是世界文明古国之一。
古埃及人只用分子为1的分数,在表示一个真分数时,将其分解为若干个埃及分数之和,
例如:7/8表示为1/2+1/3+1/24。

设计程序把一个真分数表示为最少的埃及分数之和的形式。


【想法】 一个真分数的埃及分数表示不是唯一的,例如:7/8又可以表示为1/8+1/8+1/8+1/8+1/8+1/8+1/8。
显然,把一个真分数表示为最少的埃及分数之和的形式,其贪心策略是选择真分数包含的最大埃及分数,
以7/8为例,7/8>1/2,则1/2是第一次贪心选择的结果;
7/8-1/2=3/8>1/3,则1/3是第二次贪心选择的结果;
7/8-1/2-1/3=1/24,则1/24是第三次贪心选择的结果,
即7/8=1/2+1/3+1/24。

接下来的问题是:如何找到真分数包含的最大埃及分数?设真分数为A/B,B除以A的整数部分为C,余数为D,则有下式成立:
B=AC+D

B/A=C+D/A<C+1

A/B>1/(C+1)

1/(C+1)即为真分数A/B包含的最大埃及分数。
设E=C+1,由于A/B-1/E=(AE-B)/(BE)

则真分数减去最大埃及分数后,得到真分数(AE-B)/(BE),该真分数可能存在公因子,需要化简,可以将分子和分母同时除以最大公约数。

【算法】 设函数EgyptFraction实现埃及分数问题,其算法描述如下 :
在这里插入图片描述

【程序】 主函数首先接收从键盘输入的分子A和分母B,然后调用函数EgyptFraction将该真分数表示为埃及分数之和,在表示过程中需要调用函数CommonFactor求A和B的最大公约数并对A/B进行化简。
程序如下:

#include <stdio.h>

void EgyptFraction( int A, int B ); // 埃及分数
int CommonFactor( int m, int n );	// 求最大公约数

int main(void)
{
	int A, B;
	printf("请输入真分数的分子:");
	scanf("%d", &A);
	printf("请输入真分数的分母:");
	scanf("%d", &B);
	EgyptFraction(A, B);
	return 0;
}

void EgyptFraction( int A, int B ){
	int E, R;
	printf("%d / %d = ", A, B);
	do{
		E = B/A + 1;
		printf("1/%d ", E);  //输出 1/E
		printf(" + ");
		A = A*E - B;
		B = B*E;
		R = CommonFactor(B, A);
		if(R > 1){
			A = A/R;
			B = B/R;
		}
	}while(A > 1);
	printf(" 1/%d\n", B);
	return;
}

int CommonFactor( int m, int n )
{
	int r = m%n;
	while(r != 0)
	{
		m = n;
		n = r;
		r = m%n;
	}
	return n;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

"小夜猫&小懒虫&小财迷"的男人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值