[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");
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章