2021-01-10

8数码问题

题意:在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局,找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

基本的框架不难确定,这道题目就是一个状态空间搜索的题目。运用基本的BFS就可以搞定。
把当前已经造成的局面看成一个节点,然后往下扩展就行了。
但是这道问题的关键在于如何去判重。
除了可以用集合外,我们可以把每一个状态进行编码方便我们去管理。
那问题就在于如何去进行编码。
那么就用运用散列表hash来保存 具体的哈希函数怎么去设计呢?对于这道题目就直接可以采用把他们编码为一个不超过b的一个整数
这样就可以做了。

大体框架就是这样,下面来讨论具体细节的实现。
首先是节点的扩展。
这就非常的nice了,八数码中我们可以移动的就是0的位置,所以我们应该如何找到z的坐标呢?那么根据数学规律,即可得到x=z/3;y=z%3;这是以0为下标的数组。
好的具体就可以实现了。
最有意思的是typedef的妙用以及整个数组的比较函数和赋值函数。

#include<bits/stdc++.h>
#define maxn 1000000

using namespace std;
typedef int State[9];
State st[maxn],goul={1,2,3,8,0,4,7,6,5};
int head[maxn],next[maxn];
void initLookupTable(){memset(head,0,sizeof(head));}
int hash(State& s){
	int v=0;
	for(int i=0;i<9;i++)v=v*10+s[i];
	return v%1000003;
} 
bool tryToInsert(int s){
	int h=hash(st[s]);
	int u=head[h];
	while(u){
		if(memcmp(st[u],st[s],sizeof(st[s]))==0)return false;
		u=next[u];
	}
	next[s]=head[h];
	head[h]=s;
}
int dist[maxn];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int bfs(){
	initLookupTable();
	int front=1,rear=2;
	while(front<rear){
		State &s=st[front];
		if(memcmp(goul,s,sizeof(s))==0)return front;
		int z;
		for(z=0;z<9;z++)if(!s[z])break;
		int x=z/3;int y=z%3;
		for(int i=0;i<4;i++){
			int nx=x+dx[i],ny=y+dy[i];
			int nz=nx*3+ny;
			if(nx>=0&&nx<3&&ny>=0&&ny<3){
				State& t=st[rear];
				memcpy(&t,&s,sizeof(s));
				t[nz]=s[z];
				t[z]=s[nz];
				dist[rear]=dist[front]+1;
				if(tryToInsert(rear))rear++;
			}
		}
		front++;
	}
	return 0;
}

int main(){
	char sts[9];cin>>sts;
	for(int i=0;i<9;i++)st[1][i]=sts[i]-'0';
	int ans=bfs();
	printf("%d",dist[ans]);
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值