1288 埃及分数
时间限制: 1 s
空间限制: 128000 KB
题目等级 : 钻石 Diamond
题目描述 Description
在古埃及,人们使用单位分数的和(形如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),编程计算最好的表达方式。
输入描述 Input Description
a b
输出描述 Output Description
若干个数,自小到大排列,依次是单位分数的分母。
样例输入 Sample Input
19 45
样例输出 Sample Output
5 6 18
数据范围及提示 Data Size & Hint
分类标签 Tags
搜索
/*
经典迭代搜索问题.
打了将近俩小时orz.
这题主要是剪枝(能保证正确性就剪剪剪233).
分数个数少的优,最大的分母小优.
先搞一个解答树的深度.
然后从小枚举分母.
剪枝的话.
(1) t=max(t,get_m(x,y))//当前最小分母,
第一次没加T了,原来是有个地方没return 不加也能过但要特判2333.
(2) if((maxd-d+1)*y<=x*i) return 可行性剪枝(这题就靠它了原理so easy).
(3) 剪掉不合法的最后一层 可以降常数.
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 101
#define LL long long
using namespace std;
int tot,a,b,maxd=1;
LL ans[MAXN],s[MAXN];
bool flag;
bool check(int x)
{
if(ans[maxd]>s[maxd]) return true;
for(int i=1;i<=x;i++)
if(ans[i]==ans[0]) return true;
return false;
}
int get_m(LL x,LL y)
{
tot=1;
while(x*tot<y) tot++;
return tot-1;
}
int gcd(LL x,LL y)
{
if(!y) return x;
return gcd(y,x%y);
}
void dfs(LL x,LL y,int d,int t)
{
if(d==maxd){
if(x!=1) return ;
s[maxd]=y;
if(check(maxd))
{
for(int i=1;i<=maxd;i++) ans[i]=s[i];
flag=true;
return ;
}
return ;
}
t=max(t,get_m(x,y));
for(int i=t+1;;i++)
{
if((maxd-d+1)*y<=x*i) return ;
LL xx=x*i-y,yy=y*i;s[d]=i;
int g=gcd(xx,yy);xx/=g,yy/=g;
if(d+1==maxd&&xx!=1) continue;
dfs(xx,yy,d+1,i);
}
return ;
}
int main()
{
memset(ans,127/3,sizeof ans);
scanf("%d %d",&a,&b);
for(;;maxd++)
{
dfs(a,b,1,get_m(a,b));
if(flag) break;
}
for(int i=1;i<=maxd;i++) printf("%d ",ans[i]);
return 0;
}