AcWing 1120. 埃及分数
考察:搜索
思路:
参考lrj老师的总结.当问题可以用回溯解决但明显搜索树很深时,可以考虑用迭代加深优化.
分母最小能到1e7,很明显bfs一层就有MLE的风险.考虑dfs.很明显dfs的参数需要当前分子,当前分母,目前深度,枚举开始数.这里没必要等差为1递增枚举然后判断大小,直接从最大的\(\frac 1i\) 开始枚举,当i变大,分数会逐渐减小.
在这里还有一个剪枝.因为分数是不断减小的,所以当目前的分数 \(\times\) 剩下的步数<目前需要消掉的分数 就可以直接return.这里和普通的IDA*不一样的是,当搜到结果不能立即返回true.需要继续枚举看是否有更小的分母.
AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 10000000;
int a,b,deep;
vector<LL> v,ans;
LL gcd(LL a,LL b)
{
return b?gcd(b,a%b):a;
}
int get_first(LL u,LL d)
{
return (d+u-1)/u;
}
int h(LL u,LL d,int i)
{
return (u*i+d-1)/d;
}
bool cmp(int step)
{
if(ans.size()==0) return 1;
for(int i=step;i>=0;i--)
if(v[i]!=ans[i]) return v[i]<ans[i];
return 0;
}
bool dfs(int step,LL u,LL d,int from)
{
if(step+1==deep)
{
if(d%u) return 0;
if(d/u<=v.back()) return 0;
v.push_back(d/u);
if(cmp(step)) ans = v;
v.pop_back();
return 1;
}
bool ok = 0;
from = max(get_first(u,d),from);
for(int i=from;i<=N;i++)
{
if(h(u,d,i)+step>deep) return ok;
v.push_back(i);
LL fm = d*i,fz = u*i-d;
LL g = gcd(fm,fz);
if(dfs(step+1,fz/g,fm/g,i+1)) ok = 1;
v.pop_back();
}
return ok;
}
int main()
{
scanf("%d%d",&a,&b);
while(!dfs(0,a,b,get_first(a,b))) deep++;
for(int i=0;i<ans.size();i++) printf("%d ",ans[i]);
printf("\n");
return 0;
}`
终于开始学markdown了233!
vj上有个hard版本,规定了哪些数字不能用,用bool数组标记即可.每个增加数组元素的地方都要判断否则WA.