Loading

学习笔记(2)启发式搜索

启发式搜索当中的 \(A*\)\(IDA*\),在传统的搜索算法基础上引入估价函数 \(h[x]\) 对当前结点/状态进行估价以更好地扩展路径,可以近似看作:

\(优先队列BFS/Dijkstra+h[x]\) -> \(A*\)

\(DFS\)(迭代加深)\(+h[x]\) -> \(IDA*\)

每个结点的最终“性价比\(=\) 权值 \(+\) 估价

· A*

一个好的估价函数的选择会影响程序的最终效率。任意时刻下估价函数的值小于当前结点到目标结点的实际距离,否则可能使算法失效而无法得出“最优路径”。好的估价函数会在不断搜索中逐渐变得“精确”(搜索到后面时\(h[x]\)影响逐渐变大,最终占据主导,若该函数值越接近真实值,计算出的价值也越接近实际),越精确程序效率越高

\(bfs/dijkstra\)的过程中,计算每个点的\(g[x]+h[x]\)并加入优先队列中,每次选取价值最高的点扩充路径并记录父节点(便于记录路径)即可

· IDA*

又称迭代加深的\(A*\)

对到达目标点的步数进行限制,若总花费在限定步数以内则继续\(dfs\),适用于答案在已知/较浅层的时候(可加记忆化进行优化)

· 例题

\(P1379\) 八数码难题

A*做法

#include <bits/stdc++.h>
using namespace std;
int ans,x,y;
int x_c[4]={-1,1,0,0};
int y_c[4]={0,0,-1,1};
int gol[4][4]={
	{0,0,0,0},
	{0,1,2,3},
	{0,8,0,4},
	{0,7,6,5}
};
struct sit{
	int f[4][4];
	bool operator < (const sit &b)const{
		for(int i=1;i<=3;i++){
			for(int j=1;j<=3;j++){
				if(f[i][j]!=b.f[i][j]) return f[i][j]<b.f[i][j];
			}
		}
		return 0;
	}
};
int h(sit k){
	int res=0;
	for(int i=1;i<=3;i++){
		for(int j=1;j<=3;j++){
			if(k.f[i][j]!=gol[i][j]) ++res;
		}
	}
	return res;
}
struct node{
	int d;
	sit fl;
	bool operator < (const node &b)const{
		return d+h(this->fl) > b.d+h(b.fl);
	}
}a;

priority_queue<node> q;
set<sit> s;
void bfs(){
	q.push(a);
	while(q.size()){
		node tmp=q.top(); q.pop();
		if(!h(tmp.fl)){
			printf("%d",tmp.d);
			exit(0);
		}
		for(int i=1;i<=3;i++){
			for(int j=1;j<=3;j++){
				if(tmp.fl.f[i][j]==0) x=i, y=j;
			}
		}
		for(int i=0;i<4;i++){
			int xt=x+x_c[i];
			int yt=y+y_c[i];
			if(xt<1||xt>3||yt<1||yt>3) continue;
			swap(tmp.fl.f[x][y],tmp.fl.f[xt][yt]);
			if(!s.count(tmp.fl)){
				s.insert(tmp.fl);
				q.push(node{tmp.d+1,tmp.fl});
			}
			swap(tmp.fl.f[x][y],tmp.fl.f[xt][yt]);
		}
	}
}
int main(){
	for(int i=1;i<=3;i++){
		for(int j=1;j<=3;j++){
			char ch;
			scanf("%c",&ch);
			a.fl.f[i][j]=ch-'0';
		}
	}
	a.d=0;
	bfs();
	return 0;
}

\(P2324\) 骑士精神

选取当前状态与目标状态的差距作为估价函数(由于限定步数,IDA*会稍微好写一点)

IDA*做法

#include <bits/stdc++.h>
#define N 10
using namespace std;
int t,ans,st_x,st_y;
int a[N][N];
bool success;
int x_c[8]={-2,-1,1,2,-2,-1,1,2};
int y_c[8]={-1,-2,-2,-1,1,2,2,1};
int gol[N][N]={
	{0,0,0,0,0,0},
	{0,1,1,1,1,1},
	{0,0,1,1,1,1},
	{0,0,0,2,1,1},
	{0,0,0,0,0,1},
	{0,0,0,0,0,0},
};
int h(){
	int res=0;
	for(int i=1;i<=5;i++){
		for(int j=1;j<=5;j++){
			if(a[i][j]!=gol[i][j]) ++res;
		}
	}
	return res;
}
void dfs(int dep,int x,int y,int maxdep){
	if(dep==maxdep){
		if(!h()) success=1;
		return;
	}
	for(int i=0;i<8;i++){
		int xt=x+x_c[i];
		int yt=y+y_c[i];
		if(xt<1||xt>5||yt<1||yt>5) continue;
		swap(a[x][y],a[xt][yt]);
		if(dep+h()<=maxdep) dfs(dep+1,xt,yt,maxdep);
		swap(a[x][y],a[xt][yt]);
	}
}
int main(){
	scanf("%d",&t);
	while(t--){
		for(int i=1;i<=5;i++){
			for(int j=1;j<=5;j++){
				char ch;
				scanf(" %c",&ch);
				if(ch=='*') a[i][j]=2,st_x=i,st_y=j;
				else a[i][j]=ch-'0';
			}
		}
		ans=-1;
		success=0;
		for(int i=1;i<=15;i++){
			dfs(0,st_x,st_y,i);
			if(success){
				ans=i;
				break;
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

所以总而言之其实主要的难点就是估价函数、

posted @ 2024-06-02 20:47  HRcohc  阅读(36)  评论(0编辑  收藏  举报