题目描述
时间限制: 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]);
}