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.

posted @ 2021-04-26 08:52  acmloser  阅读(32)  评论(0编辑  收藏  举报