埃及分数

题目描述

时间限制: 1 Sec 内存限制: 64 MB

在古埃及,人们使用单位分数的和(形如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

输入

第1行:2个整数,分别表示分子和分母

输出

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

样例输入

19 45

样例输出

5 6 18

思路

由于不知道搜索范围,我们可以用迭代加深搜索。
用depth来表示当前搜索分数的个数。
然后又因为保证分数的合法性,就是下面的推导式。
a:当前分子
b:当前分母
k:枚举的分母
x:相减后分子
y:相减后分母

a*k/b*k=b/k*b+x/y;   
x/y=(a*k-b)/b*k;   
a/b-1/k>=0   
a*k/b>=1   
k>=b/a  

在此范围内枚举分母,直至有解。

代码

#include<cmath>    
#include<cstdio>    
#include<cstring> 
#include<algorithm>    
using namespace std;    
#define ll long long  
const ll MAXN=1000005;   
ll flag,depth;    
ll cur[MAXN],ans[MAXN];    
ll col(ll a,ll b)   { 
    ll temp; 
    while(b){temp = b;b = a % b;a = temp;} 
    return a; 
} 
int better(ll x) 
{ 
    if(!flag)return 1; 
    for(int i=x;i>=1;i--) 
        if(cur[i]!=ans[i]) 
            return cur[i]<ans[i]||!ans[i]; 
    return 0; 
} 
void dfs(ll i,ll a,ll b)    
{    
    ll d=col(a,b);    
    a=a/d;b=b/d; 
    if(i==depth+1)return ;   
    if(a==1)    
    {   
        cur[i]=b; 
        if(!better(i))return ; 
        flag=1;  
        depth=i; 
        memcpy(ans,cur,sizeof(ll)*(i+1)); 
        return ;    
    } 
    ll t=(depth-i+1)*1.0*b;    
    for(ll k=max(cur[i-1]+1.0,ceil(b*1.0/a));k*a<t;k++)
    {
        cur[i]=k;    
        dfs(i+1,a*k-b,b*k);
    }
}   
/*   
a*k/b*k=b/k*b+x/y;   
x/y=(a*k-b)/b*k;   
a/b-1/k>=0   
a*k/b>=1   
k>=b/a   
*/
int main()    
{    
    ll a,b;    
    scanf("%lld%lld",&a,&b);    
    while(!flag&&++depth)    
        dfs(1,a,b);  
    for(ll i=1;i<depth;i++)    
        printf("%lld ",ans[i]);    
    printf("%lld\n",ans[depth]);    
}   
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值