八数码难题

link

一种名为A*的搜索方法。

A星是为了解决那些搜索树很宽很深、但答案比较浅的搜索问题。A星的核心有两个,一个是迭代加深,这一步是为了使得搜索树变浅(埃及分数那道题用了);另一个是估价函数,这是为了修剪那些不可能通向正确答案的状态,使搜索树变窄。这样一来,又窄又矮的搜索树自然跑得就快了。

迭代加深很好理解,而估价函数的含义就是快速求出一个可能的值,这个值尽量靠近最少步数但一定要不大于,同时还需要快速求得。比如这道题的估价函数设置为不同点的个数,因为每次操作都只能把一个非0点归到正确位置上,所以所用的最少步数不会比不在正确位置上的非0点的个数要少。

效果上,就这道题的数据而言,估价函数使时间从2s到200ms。

一般来说也可以用双向搜索的方式解决,而且似乎更快。但我不想写。

#include<bits/stdc++.h>
//#define feyn
using namespace std;
inline void read(int &wh){
	wh=0;int f=1;char w=getchar();
	while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
	while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
	wh*=f;return;
}
inline int max(int s1,int s2){
	return s1<s2?s2:s1;
}
inline void swap(int &s1,int &s2){
	int s3=s1;s1=s2;s2=s3;return;
}

int a[3][3],b[3][3]={{1,2,3},{8,0,4},{7,6,5}};

inline bool check(){
	for(int i=0;i<3;i++){
		for(int j=0;j<3;j++){
			if(a[i][j]^b[i][j])return false;
		}
	}
	return true;
}
inline int test(){
	int an=0;
	for(int i=0;i<3;i++){
		for(int j=0;j<3;j++){
			if(a[i][j]^b[i][j])an++;
		}
	}
	return an-1;
}
int limit,f[4][2]={{0,-1},{1,0},{-1,0},{0,1}};bool ok;
void dfs(int wh,int last,int x,int y){
	if(wh==limit){if(check())ok=true;return;}
	//if(ok||wh+test()>limit)return;
	for(int i=0;i<4;i++){
		if(i+last==3||ok)continue;
		int ax=x+f[i][0],ay=y+f[i][1];
		if(ax<0||ay<0||ax>2||ay>2)continue;
		swap(a[x][y],a[ax][ay]);
		dfs(wh+1,i,ax,ay);
		swap(a[x][y],a[ax][ay]);
	}
}

signed main(){
	
	#ifdef feyn
	freopen("in.txt","r",stdin);
	#endif
	
	int x,y;
	for(int i=0;i<3;i++){
		for(int j=0;j<3;j++){
			a[i][j]=getchar()-'0';
			if(a[i][j]==0)x=i,y=j;
		}
	}
	while(!ok){
		dfs(0,-1,x,y);
		if(ok)printf("%d",limit);
		limit++;
	}
	
	return 0;
}
posted @ 2022-07-05 16:21  Feyn618  阅读(29)  评论(0编辑  收藏  举报