埃及分数

洛谷(诡异的题目链接)

一本通在线评测

loj Linux版本

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;
 }
原文地址:https://www.cnblogs.com/Andy-park/p/13451227.html