P1763埃及分数-题解
本题解主要介绍朴素 IDA* 的解法( 360ms )
首先我们需要明白啥玩意是 IDA*
简单来说 IDA* = 迭代加深搜索 + 剪枝
由于我们不清楚本题需要多少个分子为1的分数去拼凑这个答案
所以我们需要用到迭代加深搜索
从1开始枚举深度,直到找到答案
思路
由题面我们不难看出分数是递减的
下一级分数肯定比上一级分数小
本蒟蒻采用从 a/b 减去枚举的分数,直到达到深度作为边界的方法
枚举深度的同时枚举分母,且该分数刚刚好小于上一级所减去的剩下的 a/b
(下文的 get_first 函数)
我们需要保证分子为 1
所以我们在边界的时候做处理:
1 . 当经过上一级处理剩下的 a/b 分子不为 1 ,return false
2 . 若为 1 ,则将该分母直接作为答案
此时我们得到的解肯定是最优的
( 类比 bfs )
需要用到的函数有 :
1 . gcd 函数 ( 便于约分 )
2 . better 函数 ( 更新答案 )
3 . get_firs t函数( 返回 ( 1 / c ) <= ( num / deo )的最小正整数 c )
4 . dfs 函数
兼职学习英语 :
Numerator 分子
Denominator 分母
朴素的迭代代码如下 :
( 请原谅本蒟蒻害怕溢出时刻用 long long )
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1000005;
ll a,b;
ll v[maxn],ans[maxn];
ll gcd(ll a,ll b){
if(b==0) return a;
else return gcd(b,a%b);
}
ll get_first(ll a,ll b){
for(ll i=1;;i++) if(double(1.0*1/i)<double(1.0*a/b)) return i;
}
ll better(ll dep){
if(v[dep]<ans[dep]) for(ll i=1;i<=dep;i++) ans[i]=v[i];
}
bool dfs(ll dep,ll c,ll num,ll deo,ll maxd){
if(dep==maxd){
if(deo%num) return false;//若不满足埃及分数要求,退出
v[dep]=deo/num;将该分母直接作为答案
better(dep);
return true;
}
bool ok=false;
c=max(c,get_first(num,deo));
for(ll i=c;;i++){
ll x=num*i-deo;//减去枚举到的分数
ll y=deo*i;
ll d=gcd(x,y);//约分
v[dep]=i;
if(dfs(dep+1,i+1,x/d,y/d,maxd)) ok=true;
}
return ok;
}
int main(){
scanf("%lld%lld",&a,&b);
ll tot;
ll c=get_first(a,b);
memset(ans,127,sizeof(ans));
for(ll maxd=1;;maxd++) if(dfs(1,c,a,b,maxd)){tot=maxd;break;}
//找到的话肯定是深度较浅的优
for(int i=1;i<=tot;i++) printf("%lld ",ans[i]);
return 0;
}
但是本题的数据可能不允许你用朴素的迭代写法
后果如下 :
剪枝
所以我们需要 剪枝
剪枝语句十分简单
当我们枚举分母的时候,分数随着分母增大而减少
所以当我们枚举到前面这个分母 ,此时即使后面所有的数都是该分数 ,减去仍然大于 0 , 那么继续dfs下去也没什么意义
所以只需要在枚举分母时添加上剪枝语句即可
for(ll i=c;;i++){
if(deo*(maxd-dep+1)<=num*i) break;
ll x=num*i-deo;
ll y=deo*i;
ll d=gcd(x,y);
v[dep]=i;
if(dfs(dep+1,i+1,x/d,y/d,maxd)) ok=true;
}
这样就可以 AC 啦 ~
希望这篇题解对你有所帮助和启发