7.6 迭代加深搜索 (IDA*算法实战)

本文介绍了如何使用迭代加深搜索(IDA*)算法解决埃及分数问题。通过限制搜索深度maxd并结合启发函数进行剪枝,避免了传统回溯法在无法确定解答树深度和宽度情况下的效率低下。文章详细解释了算法思路,并提供了代码实现,强调了估价函数在剪枝中的关键作用,同时分享了gcd函数的优雅递归实现。

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

7.6 迭代加深搜索 (IDA*算法实战)

大家还是直接通过问题去体会比较好。


埃及分数问题

对于一个分数a/b,将起转化为多个分子为1的分数之和,表示方式有很多种,其中加数少的比加数多的好,加数相同的情况下,则最小的分数越大越好。例如19/45=1/5+1/6+1/18是最佳方案。

分析:这道题如果用回溯法去做,解答树的深度和每一层的宽度都是无法确定的(因为每一层都是无限大的),所以显然不能用我们前面学的两种方法去做。

解决方案是采用迭代加深搜索(IDDFS):从小到大枚举深度上限maxd,每次执行只考虑深度不超过maxd的结点(DFS深度到maxd,无论找没找到结果都直接停止)。

此时我们可以构造一个类似于估价函数的方法(可能就是),在每次DFS中进行剪枝。比如当我们扩展到i层时,前i个分数之和为c/d,而第i个分数为1/e,于是确定出至少还需要(a/b-c/d)/(1/e)个分数,总和才能达到a/b,如果本次DFS此时后面的搜索次数已经小于(a/b-c/d)/(1/e),那么就可以直接跳出本次DFS。这里的关键在于:估计在某个状态至少还要多少步才能出解(启发函数)。

学术一点说,当前DFS的最大搜索深度为maxd,节点n的深度记为g(n),乐观估计函数为h(n),则当g(n)+h(n)>maxd时就要进行剪枝。这样的算法就是IDA*(如果设计出一个乐观估计函数,预测从当前结点至少还需要扩展几层结点才可能得到解,则迭代加深搜索就进化为了IDA*算法,对IDA*算法和A*算法想要做详细了解的可以去看我之前总结的这篇文章:从BFS到A*再到IDA*)。

代码实现如下(可以看做对IDA*实现的训练,非常建议大家仔细领会代码里面的一些细节,我在前后写了非常详细的注释希望能够帮助大家理解):

首先是主框架:

int ok=0,a,b; cin>>a>>b;//get_first得到的是不同分子分母下的枚举起点
	//我们之前说maxd表示每次dfs的最大深度,如果我们在某个maxd的情况下找到了解
	//这个解可能不止一个,我在这里的意思是找到了最优解
	//那么这个最优解我们在放宽的搜索深度肯定还能找到
	//所以我们在某个深度找到解了以后就可以break了,不用考虑后面会不会找到更优的解
	//并且根据迭代加深搜索的特性,maxd不仅可以表示搜索深度,在这道题还可以表示解的个数 
	for (maxd=1;;maxd++){
   memset(ans,-1,sizeof(ans)); if (dfs(0,get_first(a,b),a,b)){
   ok=1; break;} }
	if(ok){
   for(int i=0;i<=maxd;i++){
   if(i>0) cout<<"+"; printf("1/%lld",ans[i]);} }
	return 0;

我个人觉得这里的关键在于对maxd的理解,我之前认为maxd仅仅是最大的搜索深度,后来我发现迭代加深搜索的特性,它将每组解(注意这里是组)分割在了其对应的长度(某个搜索深度)之中。换句话说,maxd同时可以表示解的长度,并且不用考虑后面出现更优解的情况。

get_first函数:

//求满足1/c<=a/b的最小c,即c*a>=b
//这个函数的功能用于找到我们每一次扩展结点,新单位分数分母上的最小值
//即是每一层的枚举起点 
int get_first(ll a,ll b){
   for(int i=1;;i
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值