分数拆分
Description
任何一个分数都能才成若干个单位分数(形如1/a的, a是自然数)的和。
对于一个分数a/b,表示方法有很多种,但是哪种最好呢?
首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越好,如果还是相同,那么第二小的分数越大越好,依次类推下去。
例如对于19/45,下列方法都是合法的:
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
第一行有一个整数T,表示有T(T<=50)组测试数据,每组测试数据为一行包含a,b(0<a<b<1000)。
Output
每组测试数据若干个数,自小到大排列,依次是单位分数的分母。
Sample Input
1
19 45
Sample Output
5 6 18
/*算法思想:
刚开始的时候毫无头绪,听了讲座之后顿时大悟,用的老师讲的方法,
暴力DFS,判断当前的分数是否可行,可行的条件是:当前的分数比上
一个用过的分数小,并且假设后面所有的分数都取这个分数,求和一定
要比剩下的待合成的分数大,直到找到解,然后与作为answer的解序列
比较,取最优的,要是这个深度已经能够找到解了,不进行下一个深度的
搜索了,最后输出结果,由于怕数据太大,用的是long long保存的,剪枝的话
小剪枝,本次搜索的这个位子上的分数的分母要比作为answer的对应位子的分母小
*/
/*CODE*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100
using namespace std;
long long ans[101],now[101];
long long gcd(long long a,long long b) //求两个数的最大公约数
{
if(b==0) return a;
return gcd(b,a%b);
}
bool big(long long *a,long long *b) //判断a数组中的解序列是不是比b数组中的解序列更优
{
for(long long i=a[0];i>0;i--)
{
if(a[i]>b[i]) return true;
if(a[i]<b[i]) return false;
}
return true;
}
void cmp() //比较现在的解序列和作为answer的解序列谁更优
{
if(ans[0]==0)
{
for(long long i=0;i<=now[0];i++)
ans[i]=now[i];
return;
}
if(ans[0]<now[0]) return;
if(big(ans,now))
{
for(long long i=0;i<=ans[0];i++)
ans[i]=now[i];
}
}
/*DFS,搜索的深度,还剩下的分数的大小是: b/a 上次用的是 1/c 的单位分数*/
void dfs(long long deep,long long b,long long a,long long c)
{
if(now[0]>deep) return; //超过了枚举深度
if(b==1 && a>c) //可以直接找到解
{
now[++now[0]]=a;
cmp();
--now[0];
return;
}
long long down; //下界
if(a%b==0) down=a/b;
else down=a/b+1;
if(down<=c) down=c+1;
long long up=a*(deep-now[0])/b; //上界
if(ans[0]!=0 && up>ans[now[0]+1]) up=ans[now[0]+1]; //小优化,这次的这个位子上的分数的分母要比作为answer的对应位子的分母小
for(long long k=down;k<=up;k++)
{
now[++now[0]]=k;
long long p=gcd(b*k-a,a*k);
dfs(deep,(b*k-a)/p,a*k/p,k);
--now[0];
}
}
int main()
{
//freopen("C:/Users/hp-john/Desktop/out.txt","w",stdout);
long long t,a,b;
scanf("%lld",&t);
while(t--)
{
scanf("%lld%lld",&b,&a);
memset(ans,0,sizeof(ans));
memset(now,0,sizeof(now));
for(long long i=1;i<=100;i++)
{
dfs(i,b/gcd(a,b),a/gcd(a,b),0);
if(ans[0]>0)
{
for(long long i=1;i<ans[0];i++) printf("%lld ",ans[i]);
printf("%lld ",ans[ans[0]]);
break;
}
}
printf("\n");
}
return 0;
}