贪心算法
Posted 中学生编程与信息学竞赛自学
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了贪心算法相关的知识,希望对你有一定的参考价值。
本课程是从少年编程网转载的课程,目标是向中学生详细介绍计算机比赛涉及的编程语言,数据结构和算法。编程学习最好使用计算机,请登陆 www.3dian14.org (免费注册,免费学习)。
本节课我们继续探讨如何使用贪心算法来解决问题。今天我们讲述埃及分数的问题。
问题描述
如果一个分数的分子为1且分母为正整数,则该分数称为单位分数,例如1/3为单位分数。 每个小于1的正分数都可以表示为单位分数的总和,这种表现形式被称为埃及分数,因为它被古埃及人使用。
今天的问题就是:如何运用贪心算法生成一个正分数的埃及分数表示?
问题分析
对于给定一个小于1的正分数 f ,假设它的形式是 n/d,其中d>n, d,n 是正整数。
我们知道如果f>=1/k, 那么它一定可以包含一个单位分数1/k,这里k是大于1的整数。
在应用贪心算法时,我们总是先为分数 f 寻找小于或等于f的最大的单位分数,从中减去找到的单位分数,然后把差作为新的分数,再去寻找新分数的最大的单位分数,这样继续下去,直到最后剩下是单位分数为止。把这些找到的单位分数加起来,就是原分数 f 的埃及分数表示。
那么如何找小于或等于 f 的最大单位分数呢?
浮现于脑海中的第一种方法很简单,从1/2开始,依次把1/2,1/3,1/4,......等单位分数与分数 f 比较,一定可以找到第一个小于或等于 f 的单位分数。
请看下面的示意图。
但是这个看似简单的算法实现起来可能会有些麻烦:在计算机中,分数一般需要用浮点数表示,而浮点数比较大小的时候会有误差。
算法分析
其实有更加简便的方法。我们举例来说明。
考虑6/14,它的倒数是14/6, 我们只要找到不小于14/6的最小正整数,得到3,那么3的倒数——1/3就是当前要找的最大单位分数。
(请你想想为什么?)
接下来 6/14 减去1/3 得到 4/42,它还不是单位分数,考虑它的倒数是42/4, 不小于42/4的最小整数是11,而11的倒数1/11,就是是我们要找的第二个单位分数。
最后4/42减去1/11得到1/231,找到了最后一个单位分数。因此 6/14的埃及分数形式就是1/3+1/11+1/231。
假设有一个分数f = n/d, 其中d>n, d 和 n是正整数,过程getEF(n,d)用于求f的埃及分数表示形式。
注意在getEF(n,d)过程中有递归调用部分:getEF(n%d,d)以及getEF(n*m-d,d*m)。
这两个分支是求不大于n/d的最大单位分数。它可以对应下面的公式:
代码实现
下面是用贪心算法找埃及分数表示形式的C++代码实现。
#include <iostream>
using namespace std;
void getEF(int n, int d)
{
// 如果分子分母中有一个是0,返回
if (d == 0 || n == 0)
return;
//如果分子可以被分母整除,那么给的数就是整数,不是分数
if (n % d == 0)
{
cout << n/d ;
return;
}
// 如果分子大于分母,变成带分数的形式, 继续对分数部分求埃及分数形式
if (n > d)
{
cout << n/d << " + "; //输出整数部分
getEF(n % d, d); //对分数部分继续求埃及分数形式
return;
}
//找出比不小于d/n的最小整数m
int m;
//如果分母n可以被分子整除,那么埃及分数形式就很简单,就是商的倒数
if (d % n == 0)
{
m = d/n;
cout << "1/" << m;
}
else //如果分母不可以被分子整除
{
m = d/n + 1;
//它的倒数就是找到的第一个埃及分数
cout << "1/" << m << " + ";
//对剩余部分采用递归调用的方式继续寻找埃及分数
getEF(n * m - d, d * m);
}
}
int main()
{
int n = 6, d = 14;
cout << "The Egyptian fraction format of "
<< n << "/" << d << " is\n ";
getEF(n, d);
return 0;
}
以上是关于贪心算法的主要内容,如果未能解决你的问题,请参考以下文章