埃及分数(信息学奥赛一本通-T1444)

【题目描述】

在古埃及,人们使用单位分数的和(形如1/a的, a是自然数)表示一切有理数。如:2/3=1/2+1/6,但不允许2/3=1/3+1/3,因为加数中有相同的。对于一个分数a/b,表示方法有很多种,但是哪种最好呢?首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越好。

如:19/45=1/3 + 1/12 + 1/180

19/45=1/3 + 1/15 + 1/45

19/45=1/3 + 1/18 + 1/30,

19/45=1/4 + 1/6 + 1/180

19/45=1/5 + 1/6 + 1/18.

最好的是最后一种,因为1/18比1/180,1/45,1/30,1/180都大。

给出a,b(0<a<b<1000),编程计算最好的表达方式。

【输入】

输入:a b

【输出】

若干个数,自小到大排列,依次是单位分数的分母。

【输入样例】

19 45

【输出样例】

5 6 18

【源程序】

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#define EPS 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
const int MOD = 1E9+7;
const int N = 200+5;
const int dx[] = {-1,1,0,0,-1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;

LL maxDeep;
LL temp[N];
LL res[N];
LL GCD(LL a,LL b){
    return b==0?a:GCD(b,a%b);
}
bool judge(LL step){//当最小分数分母比原来大进行更新
    if(res[step]==-1)
        return true;
    else if(temp[step]>res[step])
        return false;
    else if(temp[step]<res[step])
        return true;
    else
        return false;
}
LL getLimit(LL x,LL y){
    for(LL i=2;;i++)
        if(y<x*i)// 1/i<=x/y
            return i;
}
bool dfs(LL step,LL minn,LL x,LL y){
	if(step==maxDeep){
		if(y%x)
            return false;
        else{
            temp[step]=y/x;
            if(judge(step))//存在更优解,更新答案
                memcpy(res,temp,sizeof(temp));
            return true;
        }
	}
	minn=max(minn,getLimit(x,y));//取得新下界,注意这里取的是max(分母),以防止漏除枚举。

	bool flag=false;
	for(LL i=minn;;i++){
		if((maxDeep-step+1)*y<=x*i)//剪枝,(maxDeep-step+1)*(1/i)<=x/y
            break;

		temp[step]=i;
		LL ny=y*i;
		LL nx=x*i-y;

		LL gcd=GCD(nx,ny);
		if(dfs(step+1,minn+1,nx/gcd,ny/gcd))
            flag=true;
	}
	return flag;
}
int main(){
    LL n,m;
    scanf("%lld%lld",&n,&m);
	for(maxDeep=1;;maxDeep++){
		LL limit=getLimit(n,m);
		memset(temp,0,sizeof(temp));
		memset(res,-1,sizeof(res));
		if(dfs(0,limit,n,m))//找到第一个可行解即退出
            break;
	}
	for(LL i=0;i<=maxDeep;i++)
        printf("%lld ",res[i]);
	return 0;
}

 

埃及分数」是指将一个真分数表示成若干个单位分数之和的形式,其中每个单位分数都是分子为1、分母为正整数的分数,并且所有单位分数互不相同。 例如: \[ \frac{2}{7} = \frac{1}{4} + \frac{1}{28} \] 这类题目常常出现在数学竞赛或算法练习题集中,尤其是在《信息学奥赛一本》这本书里。「一本 1.3 练习 1」就是关于如何求解这种类型的表达式的问题之一。对于此类问题,可以采用贪心算法来进行处理。 ### 贪心策略 为了找到给定的一个分数 \(a / b\)埃及分数展开形式,我们可以按照以下步骤操作: 1. **寻找最大的单元分数** 对于当前剩余需要分解的部分 \(\frac{a}{b}\),我们希望从中减去尽可能大的那个单一分量。即选择最小的 n 满足 \(\frac{1}{n} <= \frac{a}{b}\) ,也就是让 \(\frac{1}{x}\leqslant \frac{a}{b}, x=\lceil{\frac{b}{a}}\rceil\)(向上取整)。然后从原分数中扣除这个值作为结果的一部分; 2. **递归解决余下的部分** 减去了最大可能的单位分数之后,剩下的那部分继续应用上述规则直到全部转换完毕为止; 3. **终止条件** 当 a=0 或者已经得到了足够多的不同项,则停止计算过程并返回最终的结果列表。 过这种方法能够有效地得到一组符合条件的答案序列。需要注意的是,在某些情况下可能会有多种合法解答方案存在,但我们的目标只是找出任意一种有效路径即可满足题目要求。 下面是一个简单的 Python 实现示例: ```python def egyptian_fraction(a, b): result = [] while (a > 0 and b > 0): # 计算下一个单位分数的分母 unit_denominator = -(-b // a) # 添加到结果集 result.append(f"1/{unit_denominator}") # 更新新的未覆盖部分 lcm = b * unit_denominator updated_a = ((lcm // b) * a - (lcm // unit_denominator)) updated_b = lcm if(updated_a == 0): break; a = updated_a b = updated_b return ' + '.join(result) # 示例调用函数 print(egyptian_fraction(6, 14)) # 输出可能是 "1/3 + 1/14" ```
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值