启发式搜索--A*、IDA*算法详解

本文详细介绍了启发式搜索算法中的A*和IDA*,阐述了它们的基本思想、工作原理以及在最短路径问题中的应用。A*通过估价函数h()优化搜索顺序,IDA*则采用贪心策略结合估价函数进行剪枝,逐步逼近正确答案。此外,还提到了如何在k短路问题中使用这两种算法,并分享了相关编程实践。
摘要由CSDN通过智能技术生成
A*大意:利用一个估价函数h()确定点的遍历和更新顺序从而减少遍历次数
IDA*大意:通过贪心取答案步数,并用于与估价函数剪枝,逐步验证得出正确答案。

可能看起来有点迷,其实并不难,下面将用谁都不懂通俗易懂的方式讲解。
特点是都常用于骗分。


前置芝士:最短路和深搜

相信大家都会。


算法:

1. A*

感觉并不是很需要图啊。。。

设起点到终点的总费用为f(n),从起点到当前点的已知费用为g(n),预估当前点到终点的费用为h(n)。
显然我们可以预估f(n)=g(n)+h(n),便得到了一个总费用的近似值(先不考虑正确性)。

得到近似值之后,便可以加入一个小根的优先队列。明显总费用越小越好,所以先遍历预估总费用小的点。
第k次到达终点,便是k短路。
代码表示:(c++演示

struct data{
   
	int g,h;
	bool operator <(const data &v)const{
   
		return g+h>v.g+v.h;
	} 
}
priority_queue<data>q;

注意到当前点到终点的费用是预估而不是精确值,与广搜等遍历找最小算法不同,它体现了启发性,所以是启发式搜索。

该算法保证正确性的条件是h(n)<=h*(n),其中h*(n)为当前点到终点的确定的最小费用,h(n)>=0。

证明:它的最优性质体现在当到达终点时,h(n)<=h*(n)=0,那么h(n)=0,f(n)=g(n)+h(n)=g(n)+0=f*(n),其中f*(n)为真正的总费用,即f(n)就为最短路径。
(参考:https://bbs.csdn.net/topics/70416815)

其中当h(n)越靠近h*(n),效率越优秀。这一点可以感性理解,越靠近真实值就越准确(证明可以买专业书看
所以A*算法的运行效率取决于估价函数的优劣。

同样的,A*也可以用于求k短路问题。
因为高效率,我们可以一直跑,直到第k次到达终点,即为k短路。

当然,我们既然能够跑多次,那么跑一次最短路绰绰有余。我们不如从终点往回用dijkstra等算法得到终点到每个点的距离,即每个点的h*(n),此时估价函数与实际距离相同,必然最优。

2. IDA*

这是另外一种方向,但仍要用到上面所说的f(n),g(n),和h(n)。

我们不再直接得到费用,而是采取贪心的方法预设一个总费用,并验证是否可行,不可行就增大这个费用,重复操作,直到找到一个可行的费用为止,该费用就是答案(十分暴力

有了这个总费用,相当于给你的算法加了一个天花板,限制了天马行空的枚举。
用代码的形式写出来就是:

if(g(n)+h(n)>f(n)) return;

但别小看这一句,它能大大优化爆搜,使复杂度变得玄学而通过。
h(n)一般每个状态都计算一遍或者从上一个状态转移。

常用于数据量在两位数以内的爆搜题。


技巧:

在求k短路时,可以设置一个数组限制每个点只能到达k次,因为已经是到点x的k短路了,再从x走到终点必然最优也是k短路,所以每个点到达k次以上没有意义,可以剪枝。


代码:

1. A* 洛谷 P4467 [SCOI2007]k短路

没有办法,出题人又卡A*。。。

#include<bits/stdc++.h>
using namespace std;
int e1[10010],w1[10010],nxt1[10010],head1[110],cnt1;
int e2[10010],w2[10010],nxt2[10010],head2[110],cnt2;
int dis[110],n,m,k,a,b;
bool vis[110];
struct data1{
   
	int x,s;
	bool operator <(const data1&v)const{
   
		return s>v.s;
	}
};
priority_queue<data1>q1;
struct data2{
   
	int x,s;
	long long sta;
	vector<int>v;
	bool operator <(const data2&y)const{
   
		if(s+dis[x]!=y.s+dis[y.x])
		return s+dis[x]>y.s+dis[y.x];
		for(int i=0;i<min(v.size(),y.v.size());i++)
		if(v[i]!=y.v[i]) return</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值