题目传送门
题目大意: 将一个分数 a b \dfrac a b ba 分解成若干个单位分数 1 x \dfrac 1 x x1 的和,要求找到加数个数最少且最小的单位分数最大的方案。
显然,我们不知道搜索的深度是多少,所以我们需要使用迭代加深搜索。
但是这样还不够,难道我们每次枚举分母的时候真的要枚举到 1 0 7 10^7 107 吗?(那怕不是傻吧。。)于是,我们还需要几个优化。
首先优化分母枚举的下限。
优化1
我们设当前减去若干个单位分数后得到的分数是 x y \frac x y yx,当前枚举的分母为 i i i。
显然,一个优化就是每次的分母不可能比上一次的分母要小,于是可以从上一次的分母 + 1 +1 +1 开始枚举。
优化2
但这还不够,还有一个优化,就是每一次选取的分数不能大于 x y \frac x y yx,也就是要满足 1 i < x y \frac 1 i < \frac x y i1<yx。为了把代码写的简洁一点并且避免精度误差,我们把柿子变换一下:
1 i < x y y < x i \frac 1 i < \frac x y\\ y
于是最后只需要从这两个优化中取个最大值,就知道下限了。
接下来是优化上限。
优化3
假设我们现在还需要选 k k k 个数。
因为后面的分母会越来越大,分数会越来越小,而我们要保证剩下的这 k k k 个分数加起来是可以等于 x y \frac x y yx 的,所以,对于当前选取的分母 i i i,我们对它有这样一个要求: k i > x y \frac k i> \frac x y ik>yx,也就是说,假设剩下的 k k k 个分数全部都是 1 i \frac 1 i i1,那么这些分数加起来肯定大于 x y \frac x y yx。
如果剩下的 k k k 个数都是 1 i \frac 1 i i1 而他们加起来小于等于 x y \frac x y yx 的话,那么因为实际上这 k k k 个数一个比一个小,他们加起来一定小于 1 i \frac 1 i i1,所以它们加起来也肯定小于 x y \frac x y yx。那这样的话往下肯定搜不到答案,所以我们在这个时候要停止。
再变换一下柿子:
k i > x y k y > x i \frac k i> \frac x y\\ ky>xi ik>yxky>xi
有了这两个限制条件,我们就可以快乐地搜索了~
代码如下:
#include
#include
#include
#define int long long//懒得一个个改于是就只好暴力一点了……
int a,b;
int ans[10010],anss=0;
int s[10010],t=0;
int least(int x,int y)//求出第一个小于x/y的单位分数的分母(优化2)
{
for(int i=2;;i++)
if(x*i>y)return i;
}
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
int maxx(int x,int y){return x>y?x:y;}
bool dfs(int re,int x,int y)//re表示还要选re个数(也就是上面的k)
{
if(re==1)//当只剩最后一个数时,显然最后这个数我们只能选x/y
{
//如果x/y是个单位分数并且它比之前的解要更加优秀
if(x==1&&y>s[t]&&(anss==0||y<ans[anss]))
{
anss=++t;s[t]=y;//更新答案
for(int i=1;i<=t;i++)
ans[i]=s[i];
t--;
return true;
}
return false;
}
bool getans=false;//记录是否找到答案
//下限,两者间取个最大值
for(int i=maxx(s[t]+1,least(x,y));re*y>x*i;i++)//上限如上所述
{
int yy(y/gcd(y,i)*i);
int xx(x*(yy/y)-yy/i);//xx/yy = x/y - 1/i
s[++t]=i;//记录减去的分数的分母
if(dfs(re-1,xx/gcd(xx,yy),yy/gcd(xx,yy)))getans=true;
t--;
}
return getans;
}
#undef int
int main()
{
#define int long long
scanf("%lld %lld",&a,&b);
int gg=gcd(a,b);
a/=gg;b/=gg;
if(a==1)printf("%lld",b),exit(0);
s[0]=1;
for(int i=2;;i++)
{
t=0;
if(dfs(i,a,b))//如果找到答案就输出
{
for(int i=1;i<=anss;i++)
printf("%lld ",ans[i]);
return 0;
}
}
}