[ACM]【康托展开/BFS】HDU1043 Eight

Eight

经典的八数码问题,赛前重温顺便记录一下。
传送门
题意:3*3的方阵,由1-8的数字占据每一个格子,还有一个空格。可以通过空格改变方阵中数字的位置。输入一个方阵,求变为123/456/78的样式的移动步骤。
图

思路:

移动空格周遭的数字,其实等价于移动空格。
于是,我们把空格的位置作为状态。原本二维的位置转换成一维位置进行记录。
逆向思维,从目标状态开始BFS,每到一个新状态就记录父亲状态到新状态的移动方式,和父亲状态。这样就可以通过状态转移来得到路径。
最重要的问题是如何判重。
因为这里有9个数字,我们不可能说开一个10910^9的数组来判重,这样铁MLE。聪明的先辈们想到了一个方案:康托展开。
康托展开就是,通过数学手段,把一个数字排序,如123456789,转换为这个数字排序在9个数的全排序中的序号,在这个例子中是1。
序号最多也就9!9!个,因此,只需要开大小为9!9!以上的一维数组便能进行状态判重。

代码:

注:有所参考

#include<bits/stdc++.h>
using namespace std;
struct node1{
	char path;//记录路径 
	int fa;//父节点 
};
struct node2{//存状态 
	int aa[10];
	int n,son;//n为9在aa中的位置 
};
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}},fac[10];
node1 Node[370000];//记录每个状态的单一路径和转移方式 
//362880为全排列数目 
void set_fac(){//计算0到8的阶乘 
	fac[0]=1;
	for(int i=1;i<=8;i++)
		fac[i]=fac[i-1]*i;
} 
int cantor(int aa[]){//康托展开 
	int ans=0,k;
	for(int i=0;i<9;i++){
		k=0;
		for(int j=i+1;j<9;j++)
			if(aa[i]>aa[j])
				k++;
			ans+=k*fac[8-i];
	}
	return ans;
}
void bfs(int a[]){
	queue<node2> Q;
	node2 q,p;//q为当前点,p为下一点 
	for(int e=0;e<9;e++)
		q.aa[e]=a[e];
	q.n=8;q.son=0;
	Node[q.son].fa=0;
	Q.push(q);
	while(!Q.empty()){
		q=Q.front();Q.pop();
		for(int e=0;e<4;e++){
			p=q;
			//9的座标二维化 
			int tx=q.n%3+dir[e][0],ty=q.n/3+dir[e][1];
			if(tx>=0&&ty>=0&&tx<3&&ty<3){
				p.n=ty*3+tx;//一维化回去 
				int tem=p.aa[p.n];p.aa[p.n]=p.aa[q.n];p.aa[q.n]=tem;//把9的位置进行交换 
				p.son=cantor(p.aa);//把当前状态进行康托展开,得到排列序号 
				if(Node[p.son].fa==-1){//如果这个状态没有到过 
					Node[p.son].fa=q.son;//fa存上一个状态的排列序号 
					if(e==0) Node[p.son].path='l';//记录新路径 
					if(e==1) Node[p.son].path='r';
					if(e==2) Node[p.son].path='u';
					if(e==3) Node[p.son].path='d';
					Q.push(p);
				}
			}
		}
	}
}
int main(){
	int goal[10]={1,2,3,4,5,6,7,8,9};//终点状态 
	int start[10];//初始状态 
	for(int i=0;i<370000;i++)
		Node[i].fa=-1;
	set_fac();
	bfs(goal);
	char ch[50];
	while(gets(ch)>0){
		for(int i=0,j=0;ch[i]!='\0';i++){
			if(ch[i]=='x')
				start[j++]=9;
			else if(ch[i]>='0'&&ch[i]<='8')
				start[j++]=ch[i]-'0';
		}
		int s=cantor(start);
		if(Node[s].fa==-1){
			printf("unsolvable\n");
			continue;
		}
		while(s!=0){
			printf("%c",Node[s].path);
			s=Node[s].fa;
		}
		printf("\n");
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章