EOJ 1154/ UVA 12558 埃及分数问题 迭代加深搜索

本文介绍了一种将任意分数拆解为若干个埃及分数之和的算法,并详细探讨了使用迭代加深搜索策略来寻找最优解的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们称呼分子为1的分数为埃及分数

对于把一个分数拆解成n个埃及分数相加的形式的方法肯定是多种多样的,例如 59/211 = 1/4 + 1/36 + 1/633 + 1/3798 = 1/6 + 1/9 + 1/633 + 1/3798.那么我们会选择哪种呢?

partychen告诉我们:

1.分解所需要的数越少越好

2.当1相同时,最后一个分数越大越好,即分母越小越好

3.当1,2相同时,这些分数的分母字典序越小越好。

那么现在就来思考算法了。很容易想到的思路。

现在有一个分数a/b。小于等于a/b的埃及分数有1/k, 1/(k+1), 1/(k+2).....

我们先将1/k拆分出去,然后继续拆分a/b - 1/k,如果我们发现这条路是行不通的。我们就试试先将1/(k+1)拆分出去,然后继续拆分a/b - 1/(k+2)。如果我们发现这条路还是行不通的,就换1/(k+3)........总是能找到答案的。

即拆分a/b,我们先尝试第一个小于等于a/b的埃及分数,再尝试第二个小于等于a/b的埃及分数,依次类推, 直到原分数拆分成0为止。

这种思路不就是dfs吗,它的解答树正好是按照深度优先的规则来遍历的。

想到这里我们就可以很高兴的写dfs了,但是写出来肯定会发现不对劲的。在写dfs之前我们应该先估计一下解答树的深度上限,不然有可能会爆栈。

对于这道题,我们每次都选择第一个小于它的埃及分数来拆分。首先我们要明白,任何一个分数总是能够找到第一个小于它的埃及分数的,换句话说解答树中的每个节点都有子节点。即解答树的深度是无穷大的。

如果我们陷入到了一种很坏的情况,虽然不至于搜索到无穷大,也会搜索到很深很深。这样是不可行的。

现在的问题在哪?dfs我们会搜索到很深。也就是解答树的深度没有明显的上界。这时候就要引入一种新的搜索方法,我们叫它迭代加深搜索。

迭代加深搜索和dfs基本相同,只不过我们限制了它的搜索的最大深度。假如一个分数最好的方案是拆解成三个埃及分数相加,其实也就是dfs我们要搜索三层。

那么按照迭代加深的思路,就是我们先限制搜索层数是0层,无解。1层,无解。2层,无解。3层,有解。只要这个解是存在的,通过深度不断增加的dfs,我们肯定可以找到它。

迭代加深有什么好处呢?

1.避免了因为深度没有明显上界,使得搜索很慢很慢的情况。

2.继承了dfs耗费空间少的特点。

3.因为我们设置了深度上限,在得知我们当前深度的情况下,我们可以利用已知信息剪枝。举个不恰当的例子,假如老师叫我们三天写1000道题目。我们有多种方法可以完成这个任务。按照a方法到了第二天晚上的时候,我们只做了50道。根据已有的上界:3天1000道题目,和我们当前的深度:直到第二天晚上只做了50道题目,还需要的任务量:6个小时做950道题目。我们就可以得出结论:这任务按照你之前的速度没法完成,要想完成老师的任务你要试试其他方法。比如我们可以试试b方法:直接copy同学作业,哈哈。

还有不懂的地方可以看一下网上关于迭代加深的讲解。代码如下:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>

using namespace std;

typedef long long int LL;
const LL INF = 0x3f3f3f3f;
const LL maxn = 1005;
LL maxd;//maxd表示当前我们限制的深度上限
LL ANS[maxn], ans[maxn];

LL gcd(LL x, LL y){return y == 0 ? x : gcd(y, x % y);}

bool is_better()//判断现在的答案是不是比之前的答案更优
{
    if (ans[maxd] != ANS[maxd]) return ans[maxd] < ANS[maxd];
    bool res = true;
    for (int i = 0; i <= maxd - 1; i++){
        if (ans[i] > ANS[i]){
            res = false;
            break;
        }
    }
    return res;
}

bool dfs(LL a, LL b, LL level, LL d){
    LL gc = gcd(a, b);
    a = a / gc;
    b = b / gc;

    if (d == maxd){
        if (a == 1 && b >= level){
            ans[d] = b;
            if (is_better()) memcpy(ANS, ans, sizeof(ans));
            return true;
        }
        return false;
    }

    bool res = false;
    for (int i = level; ; i++){
        if ((maxd - d + 1) * b <= a * i) break;//因为我们拆分用到的埃及分数是越来越小的,也就是在之后的遍历中我们使用的分数是
        //小于1/i的,现在假设最好情况,每次我们都用1/i。结果发现就算我们每次都用最大的,也无法把已知分数拆分成0.这还有什么继续的必要
        //呢?所以不用再搜索了,剪枝!

        if (a * i >= b){// 1/i <= a/b 可以用来拆分 
            ans[d] = i;
            if (dfs(a * i - b, b * i, i + 1, d + 1)) res = true;
        }
    }

    return res;
}

int main()
{
    //freopen("1.txt", "r", stdin);
    LL a, b;
    while (~scanf("%I64d%I64d", &a, &b)){
        memset(ANS, INF, sizeof(ANS));
        for (maxd = 0; ; maxd++){//深度上限递增,我们总能找到答案
            if (dfs(a, b, 1, 0))
                break;
        }

        for (int i = 0; i <= maxd; i++)
            printf("%I64d%c", ANS[i], i < maxd ? ' ' : '\n');
    }
    return 0;
}


### 关于EOJ DNA排序问题的解题思路 在处理EOJ中的DNA排序问题时,主要挑战在于如何高效地完成字符串数组的排序以及去重操作。由于题目涉及两个测试点可能因时间复杂度较高而超时,因此需要优化算法设计。 #### 数据结构的选择 为了降低时间复杂度并提高效率,可以引入`std::map`或者`unordered_map`来辅助实现去重功能[^1]。这些数据结构能够快速判断某项是否存在集合中,并支持高效的插入和查找操作。具体来说: - 使用 `std::set` 可以自动去除重复元素并对结果进行升序排列; - 如果还需要自定义比较逻辑,则可以选择基于哈希表的数据结构如 `unordered_set` 配合手动排序。 #### 排序策略 对于给定的一组DNA序列(通常表示为长度固定的字符串),按照字典顺序对其进行排序是一个常见需求。C++标准库提供了非常方便的方法来进行此类任务——即利用 `sort()` 函数配合合适的比较器函数对象或 lambda 表达式来指定所需的排序规则。 下面展示了一个简单的例子用于说明如何读取输入、执行必要的预处理步骤(包括但不限于删除冗余条目),最后输出经过整理的结果列表: ```cpp #include <bits/stdc++.h> using namespace std; int main(){ set<string> uniqueDNAs; string line, dna; while(getline(cin,line)){ stringstream ss(line); while(ss>>dna){ uniqueDNAs.insert(dna); // 自动过滤掉重复项 } } vector<string> sortedUnique(uniqueDNAs.begin(),uniqueDNAs.end()); sort(sortedUnique.begin(),sortedUnique.end()); for(auto it=sortedUnique.cbegin();it!=sortedUnique.cend();++it){ cout<<*it; if(next(it)!=sortedUnique.cend())cout<<" "; } } ``` 上述程序片段实现了基本的功能模块:从标准输入流逐行解析得到各个独立的DNA片段;借助 STL 容器特性轻松达成无重复记录维护目的;最终依据字母大小关系重新安排各成员位置后再统一打印出来[^3]。 #### 学习延伸至自然语言处理领域 值得注意的是,在计算机科学特别是机器学习方向上,“上下文”概念同样重要。例如 Word2Vec 这样的技术就是通过考察周围词语环境来捕捉特定词汇的意义特征[^2]。尽管两者应用场景差异显著,但从原理层面看均体现了对局部模式挖掘的关注。 ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值