Solution
因为不知道分数个数,可以无限凑,所以考虑迭代加深搜索。每次枚举一个 std,如果当前步数超过 std 就 return;一旦用 std 步成功凑出就 break,输出结果。
需要注意的是,同一个 std 第一次找到的是“字典序最小”的解,而不是“最大数最小的解”。所以对于每一个 std,都需要找到所有可能的解,判断取出最大值最小的一个。
现在 dfs 枚举分母的上下界还未确定。设当前还有 (frac{a}{b}) 的分数未凑出,当前枚举的分母为 (i):
下界
(1.) 一个显然的优化就是每次按照递增顺序枚举,所以初始下界应该是 last+1。
(2.) 如果分母 (i) 过小,(i) 的倒数超过了 (frac{a}{b}),继续搜下去是没有意义的,这种情况需要裁掉。(frac{1}{i}>frac{a}{b}) 即 (i>frac{b}{a})。
总结一下上面两条,循环的下界是 (max(last+1, frac{b}{a}))。
上界
因为分数的个数已确定为 (std),设当前还能放 (p) 个分数;如果分母过大,分数过小,且往后的每一个分数都比它小,会导致 (p) 个分数凑完一定小于输入的分数。所以 (p*frac{1}{i}>a/b),这个式子也可以写成 (i<frac{b*p}{a})。这样就得出了循环的上界。
到这里程序就基本写完了。但是要注意循环上界的小于号(不是小于等于),它不允许最后一个分数加进来;因此我们在上一步就应该提前计算出最后一个分数,判断它是否合法。
Code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 233333;
struct Add { long long a, b; } X;
long long res = 1e17, suc = 0, id = 0, ans[N], use[N];
long long gcd(long long x, long long y)
{
if(x < y) swap(x, y);
if(x % y == 0) return y;
else return gcd(y, x % y);
}
long long lcm(long long x, long long y) { return (x * y) / gcd(x, y); }
Add ad(Add x, Add y, long long k)
{
Add tmp;
long long lc = lcm(x.a, y.a);
tmp.a = lc;
if(k == 0)
tmp.b = (lc / x.a) * x.b + (lc / y.a) * y.b;
else
tmp.b = (lc / x.a) * x.b - (lc / y.a) * y.b;
long long gc = gcd(tmp.a, abs(tmp.b));
if(gc != 1)
{
tmp.a /= gc;
tmp.b /= gc;
}
if(tmp.a == 0) tmp.a = 1;
return tmp;
}
void dfs(Add sum, int std, int stp)
{
Add st = ad(X, sum, 1);
if(st.b <= 0) return ;
if(stp == std)
{
if(st.b != 1 || st.a < use[std - 1]) return ;
use[stp] = st.a;
if(res > use[std])
{
res = use[std];
suc = 1;
for(int i = 1; i <= std; i++)
ans[i] = use[i];
}
return ;
}
for(int i = max(use[stp - 1] + 1, st.a / st.b + 1); i < (std - stp + 1) * st.a / st.b; i++)
{
Add now;
now.a = i, now.b = 1;
use[stp] = i;
dfs(ad(now, sum, 0), std, stp + 1);
}
}
int main()
{
cin >> X.b >> X.a;
memset(use, 0, sizeof(use));
memset(ans, 0, sizeof(ans));
long long x = gcd(X.b, X.a);
X.a /= x;
X.b /= x;
for(int i = 2; i <= 233; i++)
{
Add fir;
res = 1e16;
fir.a = 1, fir.b = 0;
dfs(fir, i, 1);
if(suc) { id = i; break; }
}
for(int i = 1; i <= id; i++) cout << ans[i] << " ";
cout << endl;
return 0;
}