洛谷题单指南-进阶搜索-P1763 埃及分数
原题链接:https://www.luogu.com.cn/problem/P1763
题意解读:将分数a/b拆分成若干个1/x的和,要求x递增,且拆分的个数越少越好,个数相同时最后一个x越小越好。
解题思路:
首先,比较明显的是可以用搜索来解决问题。
但是,如果用常规的DFS,搜索的深度有可能很深,因为每一个分母的最大值可以取到1e7,可能会爆栈;再考虑BFS,每一层状态数量也是巨大的,又可能会使得队列爆内存。
既然要求产分的个数越少越好,就可以想到对于搜索深度进行限制,逐渐递增,即迭代加深搜索。
通过每次枚举一个拆分项的分母,从a/b减掉该项后不断递归。
有几种重要的剪枝:
1、如果搜索深度超过当前最大深度,则返回
2、每次对拆分项分母枚举时,应该至少比上一次的大,并且a/b >= 1/x,所以x的枚举起点是max(上一次的分母+1,b/a+1)
3、每次枚举的拆分项,显然是后面结果中最大,如果接下来所有拆分项都是这个值,加起来都<=a/b,那么显然无解,也就是不满足(maxdepth-depth+1)*1/x<=a/b时可以提前结束枚举,加上此剪枝后,算法就有迭代加深变成了启发性搜索IDA*
仅有以上优化,还无法得到100分,此题还有一个关键的优化:对分母最大值的限制:1e7
如果每次都判断分母不超过1e7,也是比较耗时,这里就引入一种双重迭代加深方法:
第一层,针对搜索深度进行迭代加深,每次加1
第二层,针对分母最大限制进行迭代加深,初始1000,每次乘10
这样在dfs过程中,如果当前分数的分母b>最大分母限制,就可以提前结束了,这是因为a/b如果b超过maxb,那么a/b拆解出最后一个分数1/x的x必然大于maxb。
小知识:为什么迭代加深搜索不会超时?
迭代加深从限制深度1开始,每次加1,不会超时,因为假设在深度k的时候找到答案,且每一层都是满状态,以满二叉树为例,最后一层状态数为2^(k-1),而前k-1层状态数加起来也不过2^k-1 - 2^(k-1),还是在2^(k-1)级别,与最后一层复杂度同级别,所以最后一层不超时的情况,整体并不会导致超时。
100分代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a, b;
vector<int> tmp, ans;
ll gcd(ll a, ll b)
{
return b == 0 ? a : gcd(b, a % b);
}
//a/b表示当前的分数,depth表示当前深度,start表示下一个加数的分母的起始值,maxb表示分母的最大值,maxdepth表示最大深度
void dfs(ll a, ll b, int depth, ll start, int maxb, int maxdepth)
{
if(depth > maxdepth) return; //剪枝,如果当前深度大于最大深度,那么就不用继续搜索了
ll d = gcd(a, b);
a /= d, b /= d; //将a和b约分
if(b > maxb) return; //***剪枝,如果b大于最大分母,那么就不用继续搜索了
if(a == 1 && b > tmp.back()) //最后一个加数的分母必须是最大的
{
tmp.push_back(b);
if(ans.size() == 0 || (tmp.size() == ans.size() && tmp.back() < ans.back())) ans = tmp;
tmp.pop_back();
return;
}
start = max(start, b / a + 1); //剪枝,从b/a+1开始搜索,这样可以避免重复搜索
for(int i = start; ; i++) //枚举下一个加数的分母
{
if(b * (maxdepth - depth + 1) < a * i) break; //剪枝,如果当前深度到最大深度的剩余搜索空间都不够,那么就不用继续搜索了
if(i > maxb) break; //剪枝,如果当前分母大于最大分母,那么就不用继续搜索了
tmp.push_back(i);
dfs(a * i - b, b * i, depth + 1, i + 1, maxb, maxdepth);
tmp.pop_back();
}
}
int main()
{
cin >> a >> b;
for(int maxdepth = 1; maxdepth <= 10; maxdepth++) //迭代加深最大深度
{
for(int maxb = 1000; maxb <= 10000000; maxb *= 10) //迭代加深分母的最大值
{
dfs(a, b, 1, 1, maxb, maxdepth);
if(ans.size()) break; //如果找到解,那么就不用继续增加最大分母
}
if(ans.size()) break; //如果找到解,那么就不用继续增加最大深度
}
for(auto v : ans) cout << v << " ";
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具