埃及分数(迭代加深搜索,斐波那契分解分数)

文章目录

        • 题目
        • 思路
        • 代码


题目

描述
在古埃及,人们使用单位分数的和(形如 1 a \frac{1}{a} a1的, a a a是自然数)表示一切有理数。如: 2 3 = 1 6 + 1 2 \frac{2}{3} = \frac{1}{6} + \frac{1}{2} 32=61+21,但不允许 2 3 = 1 3 + 1 3 \frac{2}{3} = \frac{1}{3} + \frac{1}{3} 32=31+31,因为加数中有相同的。对于一个分数 ,表示方法有很多种,但是哪种最好呢?首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越好。如:
19 45 = 1 3 + 1 12 + 1 180 \frac{19}{45} = \frac{1}{3}+\frac{1}{12}+\frac{1}{180} 4519=31+121+1801

19 45 = 1 3 + 1 15 + 1 45 \frac{19}{45} = \frac{1}{3}+\frac{1}{15}+\frac{1}{45} 4519=31+151+451

19 45 = 1 3 + 1 18 + 1 30 \frac{19}{45} = \frac{1}{3}+\frac{1}{18}+\frac{1}{30} 4519=31+181+301

19 45 = 1 4 + 1 6 + 1 180 \frac{19}{45} = \frac{1}{4}+\frac{1}{6}+\frac{1}{180} 4519=41+61+1801

19 45 = 1 5 + 1 6 + 1 18 \frac{19}{45} = \frac{1}{5}+\frac{1}{6}+\frac{1}{18} 4519=51+61+181
最好的是最后一种,因为 1 18 \frac{1}{18} 181 1 180 , 1 30 , 1 45 \frac{1}{180},\frac{1}{30},\frac{1}{45} 1801,301,451都大。
注意,可能有多个最优解。如:
59 211 = 1 4 + 1 36 + 1 633 + 1 3798 \frac{59}{211} = \frac{1}{4}+\frac{1}{36}+\frac{1}{633}+\frac{1}{3798} 21159=41+361+6331+37981

59 211 = 1 6 + 1 9 + 1 633 + 1 3798 \frac{59}{211} = \frac{1}{6}+\frac{1}{9}+\frac{1}{633}+\frac{1}{3798} 21159=61+91+6331+37981

由于方法一与方法二中,最小的分数相同,因此二者均是最优解。

给出 a , b a,b ab编程计算最好的表达方式。保证最优解满足:最小的分数 ≥ 1 1 0 7 \ge \frac{1}{10^7} 1071
输入
一行两个整数,分别为 a a a b b b的值。
输出
输出若干个数,自小到大排列,依次是单位分数的分母。
样例输入
19 45
样例输出
5 6 18
数据范围
0 < a < b < 1000 0 < a<b<1000 0<a<b<1000

思路

在解这道题之前,我们先来了解一下斐波那契分解分数
a , b a,b a,b互质,其中 a < b a<b a<b,分数 a b \frac{a}{b} ba可以这样分:
⌊ b a ⌋ = p 1 , r 1 = b % a , a b = a a ∗ p 1 + r 1 \left \lfloor \frac{b}{a} \right \rfloor = p_1,r_1 = b\% a, \frac{a}{b} = \frac{a}{a*p_1+r_1} ab=p1r1=b%a,ba=ap1+r1a而我们想要把 a b \frac{a}{b} ba化为 1 m \frac{1}{m} m1和另外一个分数的和,那么显然我们要把分子化成 a ∗ p 1 + r 1 a*p_1+r_1 ap1+r1加上另一坨,所以 a b = a a ∗ p 1 + r 1 = a ∗ p 1 + r 1 − r 1 ( a ∗ p 1 + r 1 ) ∗ p 1 = 1 p 1 − r 1 b ∗ p 1 \frac{a}{b} = \frac{a}{a*p_1+r_1}=\frac{a*p_1+r1-r1}{(a*p_1+r_1)*p_1}=\frac{1}{p_1}-\frac{r_1}{b*p_1} ba=ap1+r1a=(ap1+r1)p1ap1+r1r1=p11bp1r1,而我们想要加法,显然 r 1 < a r_1 < a r1<a,所以我们要在 a ∗ p 1 + r 1 − r 1 ( a ∗ p 1 + r 1 ) ∗ p 1 \frac{a*p_1+r_1-r_1}{(a*p_1+r_1)*p_1} (ap1+r1)p1ap1+r1r1的分子加个 a a a,就分子变成了 a ∗ p 1 + a = a ∗ ( p 1 + 1 ) a*p_1+a=a*(p_1+1) ap1+a=a(p1+1),所以在分母上也要乘上 p 1 + 1 p_1+1 p1+1,就等于 a ∗ p 1 + a + r 1 − r 1 ( a ∗ p 1 + r 1 ) ∗ ( p 1 + 1 ) = 1 p 1 + 1 + a − r 1 b \frac{a*p_1+a+r_1-r_1}{(a*p_1+r_1)*(p_1+1)}=\frac{1}{p_1+1}+\frac{a-r_1}{b} (ap1+r1)(p1+1)ap1+a+r1r1=p1+11+bar1
准确的将该算法表述应该是这样的:
设 某 个 真 分 数 的 分 子 为 a , 分 母 为 b ; 把 b 除 以 a 的 商 部 分 加 1 后 的 值 作 为 埃 及 分 数 的 某 一 个 分 母 c ; 将 a 乘 以 c 再 减 去 b , 作 为 新 的 a ; 将 b 乘 以 c , 得 到 新 的 b ; 如 果 a 大 于 1 且 能 整 除 b , 则 最 后 一 个 分 母 为 b / a ; 算 法 结 束 ; 或 者 , 如 果 a 等 于 1 , 则 最 后 一 个 分 母 为 b ; 算 法 结 束 ; 否 则 重 复 上 面 的 步 骤 。 设某个真分数的分子为a,分母为b;\\ 把b除以a的商部分加1后的值作为埃及分数的某一个分母c;\\ 将a乘以c再减去b,作为新的a;\\ 将b乘以c,得到新的b;\\ 如果a大于1且能整除b,则最后一个分母为b/a;算法结束;\\ 或者,如果a等于1,则最后一个分母为b;算法结束;\\ 否则重复上面的步骤。 abba1cacbabcba1bb/aa1b
至于这个搜索深度啊,经各老师试出来之后,6,7都可以过,所以自行斟酌吧,
除了这些,我们还要确定搜索的上下限
因为 a b ≥ 1 i \frac{a}{b} \ge \frac{1}{i} bai1,所以我们的分母要 i ≥ b a i\ge \frac{b}{a} iab,而如果 i < 前 一 个 搜 索 出 的 答 案 i < 前一个搜索出的答案 i<,我们就要把 i i i更新成前一个搜索出的答案+1,因为答案要求升序,这是下限。
因为 a b ≤ 剩 下 深 度 个 1 i + 1 i + ⋯ + 1 i ⏞ \frac{a}{b} \leq \begin{matrix} 剩下深度个 \\ \overbrace{ \frac{1}{i}+\frac{1}{i}+\cdots+\frac{1}{i}}\end{matrix} bai1+i1++i1 ,所以 i ≤ b ∗ 剩 下 深 度 a i\leq\frac{b*剩下深度}{a} iab,如果已有最优解,那么 i i i一定要小于最优解的分母,最后注意细节就行了

代码

#include 
#include 
#include 
#include 
using namespace std;
#define LL long long

LL s[7], cnt, ans[7], len = 10;
LL a, b, m_depth;

bool flag;

inline LL gcd(LL a, LL b){
    if( !b )
        return a;
    return gcd(b, a%b);
}

inline void IDDFS(LL a, LL b, LL k){
    if( k > m_depth )
        return ;
    if( b%a == 0 && b/a > s[k-1] ){
        s[k] = b/a;
        if( !flag || s[k] < ans[k] )
            memcpy(ans,s,sizeof(s));
        flag = 1;
        return;
    }
    LL q = b/a;
    if( q <= s[k-1] )
        q = s[k-1]+1;
    LL t = b*(m_depth-k+1)/a;
    if( flag && t >= ans[m_depth] )
        t = ans[m_depth]-1;
    for(LL i = q; i <= t; i ++){
        LL m = gcd(i*a-b, b*i);
        s[k] = i;
        IDDFS((i*a-b)/m,b*i/m,k+1);
    }
}

int main(){
    scanf("%lld%lld", &a, &b);
    LL d = gcd(a,b);
    a /= d;
    b /= d;
    for(m_depth = 1; m_depth <= 10; m_depth ++){
        IDDFS(a, b, 0);
        if( flag ){
            printf("%lld", ans[0]);
            for(int i = 1; i <= m_depth; i ++)
                printf(" %lld", ans[i]);
            break;
        }
    }
    return 0;
}

你可能感兴趣的:(迭代加深)