埃及分数

本文介绍了古埃及分数的表示方法,强调了在寻找最优表示时,加数少且最小分数大的原则。通过举例展示了如何确定分数的最佳组合。并提出了一个编程问题,要求计算单位分数之和的最优解,其中最小分数大于等于10^7。为了解决这个问题,提出了迭代加深的搜索策略,结合深度优先搜索(DFS)和广度优先搜索(BFS),同时引入剪枝策略以限制枚举范围,确保在大数据情况下也能有效求解。

在古埃及,人们使用单位分数的和(形如1a\frac{1}{a}a1的,aaa是自然数)表示一切有理数。
如:23=12+16\frac{2}{3}=\frac{1}{2} + \frac{1}{6}32=21+61,但不允许23=13+13\frac{2}{3}=\frac{1}{3} + \frac{1}{3}32=31+31,因为加数中有相同的。
对于一个分数ab\frac{a}{b}ba,表示方法有很多种,但是哪种是最好的呢?

首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越好。

如:
1945=13+112+1180\frac{19}{45}=\frac{1}{3}+\frac{1}{12}+\frac{1}{180}4519=31+121+1801
1945=13+115+145\frac{19}{45}=\frac{1}{3}+\frac{1}{15}+\frac{1}{45}4519=31+151+451
1945=13+118+130\frac{19}{45}=\frac{1}{3}+\frac{1}{18}+\frac{1}{30}4519=31+181+301
1945=14+16+1180\frac{19}{45}=\frac{1}{4}+\frac{1}{6}+\frac{1}{180}4519=41+61+1801
1945=15+16+118\frac{19}{45}=\frac{1}{5}+\frac{1}{6}+\frac{1}{18}4519=51+61+181

最好的是最后一种,因为118\frac{1}{18}1811180\frac{1}{180}1801145\frac{1}{45}451130\frac{1}{30}3011180\frac{1}{180}1801都大。

注意,可能有多个最优解。

如:
59211=14+136+1633+13798\frac{59}{211} = \frac{1}{4} + \frac{1}{36} + \frac{1}{633} + \frac{1}{3798}21159=41+361+6331+37981

59211=16+19+1633+13798\frac{59}{211} = \frac{1}{6} + \frac{1}{9} + \frac{1}{633} + \frac{1}{3798}21159=61+91+6331+37981

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

给出a,ba, ba,b,编程计算最好的表达方式。保证最优解满足:最小的分数≥1107\geq \frac{1}{10^7}1071

输入格式

一行两个整数,分别为a和b的值

输出格式

输出若干个数,自小到大排列,依次是单位分数的字母。

数据范围

0<a<b<10000<a<b<10000<a<b<1000

输入样例:
19 45
输出样例:
5 6 18

做法:

迭代加深

迭代加深就是一种深搜和广搜的结合,有些题目写的时候有些状态会一直往下做而超时,所以我们可以设定一个深度,做到这个深度就返回,如果找不到答案就继续做。

就像这样:
在这里插入图片描述
没找到答案,继续找
D1FD3t.md.png
找到了答案就停止,并输出答案
这种方法就是DFS的空间BFS的时间就可以把答案算出来了。

然后在搜索里面从小到大枚举字母字母的数值,
但是需要满足两个条件:

  1. 要保证枚举出来的从小到大
  2. 最大的字母越小越好
    但是aaabbb都是>1000>1000>1000

所以要加个剪枝来减小枚举分母的范围,如果要分解ab\frac{a}{b}ba,还剩x个数
则最大的分母不会超过ceil(bxa\frac{bx}{a}abx)

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define LL long long
#define MAXN 1005
int maxd;
int ans[MAXN], tmp[MAXN];
bool cmp(int d)
{
    for(int i = d; i >= 0; i--)
        if(ans[i] != tmp[i])
            return (ans[i] == 0 || tmp[i] < ans[i]) && (tmp[i] > 0);
    return false;
}
LL gcd(LL a, LL b)
{
    return b == 0 ? a : gcd(b, a % b);
}
bool dfs(int d, LL last, LL a, LL b)
{
    if(d == maxd)
    {
        if(b % a)
            return false;
        tmp[d] = b / a;
        if(cmp(d))
            memcpy(ans, tmp, sizeof(ans));
        return true;
    }
    bool flag = false;
    last = max(last, b / a + 1);
    for(int i = last; ;i++)
    {
        if(b * (maxd + 1 - d) <= i * a)   //剪枝
            break;
        tmp[d] = i;
        LL a1 = a * i - b, b1 = b * i;
        LL t = gcd(a1, b1);
        if(dfs(d + 1, i + 1, a1 / t, b1 / t))
            flag = true;
    }
    return flag;
}
int main()
{
    LL a, b;
    scanf("%lld %lld", &a, &b);
    for(maxd = 2; ; maxd++)
        if(dfs(0, (b / a + 1), a, b))
            break;
    for(int i = 0; i <= maxd; i++)
        printf("%d ", ans[i]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值