1. 程式人生 > >藍橋杯 歷屆試題 九宮重排(bfs)

藍橋杯 歷屆試題 九宮重排(bfs)

問題描述  如下面第一個圖的九宮格中,放著 1~8 的數字卡片,還有一個格子空著。與空格子相鄰的格子中的卡片可以移動到空格中。經過若干次移動,可以形成第二個圖所示的局面。

  我們把第一個圖的局面記為:12345678.
  把第二個圖的局面記為:123.46758
  顯然是按從上到下,從左到右的順序記錄數字,空格記為句點。
  本題目的任務是已知九宮的初態和終態,求最少經過多少步的移動可以到達。如果無論多少步都無法到達,則輸出-1。輸入格式  輸入第一行包含九宮的初態,第二行包含九宮的終態。輸出格式  輸出最少的步數,如果不存在方案,則輸出-1。樣例輸入12345678.
123.46758樣例輸出3樣例輸入13524678.
46758123.樣例輸出

22

【解題思路】

這道題用了bfs+康拓判重

bfs既廣度優先搜尋,搜到的第一個就是最少步數。如何判斷重複是一個問題,有很多人用雜湊表雜湊函式,本人學藝不精不會使用雜湊(捂臉),康拓展開是數學上的一個公式,這個公式運用於判斷一個數在該數全排列下第幾個。

定義:X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!

eg:數213在123的全排列下面是第X+1個數

        2的後面有1個數比2小,既1*2!

        1的後面沒有比1小的數,既0*1!

        3的後面沒有比3小的數,既0*0!

        X = 1*2!+0*1!+0*0! = 2

        123的全排列: 123  132  213 231 312 321

九宮最多有9!種排列組合,既362880中排列組合方式,使用陣列state[362880]可以用來判斷是否出現重複的元素。

使用bfs搜尋需要使用佇列這一資料結構,曾嘗試過使用STL中的queue但是失敗了(捂臉),下面程式碼使用得是佇列先進先出的特性。

#include<iostream>
#include<string>
#include<cstring>
using namespace std;

int state[362880];  //判斷是否重複
int step[362880];  //儲存步數
int bnum,bkt;
int dx[] = {-1,1,0,0};  //上下左右
int dy[] = {0,0,-1,1};
typedef int type[9];
type q[362880];
type a,b;  //a為初始狀態,b為最終狀態
type tem,tem2;

int kangtuo(int x[])  //康拓展開判斷是否重複
{
	int fac[]={1,1,2,6,24,120,720,5040,40320};
	int i,j,t,sum;
	sum = 0;
	for(i=0;i<9;i++)
	{
		t = 0;
		for(j=i+1;j<9;j++)
		{
			if(x[j]<x[i])
				t++;
		}
		sum = sum+t*fac[8-i];
	}
	if(state[sum]==1)
		return 0;
	else
	{
		state[sum] = 1;
		return 1;
	}
}

int bfs()
{
	//bfs廣度優先搜尋,搜尋到的第一個就是最短路徑
	memcpy(q[0],a,sizeof(a));  //初始狀態入隊
	int front = 0;
	step[front] = 0;
	int rear = 1;
	int i,j,p,x,y,xx,yy;
	//type tem,tem2;
	while(front < rear)  //如果佇列不為空
	{
		memcpy(tem,q[front],sizeof(tem));
		if(memcmp(tem,b,sizeof(b)) == 0)  //如果隊首元素與最終元素相等
			return front;
		for(i=0;i<9;i++)  //找到0的位置
		{
			if(tem[i] == 0)
				break;
		}
		x = i/3;  //轉換成二維座標
		y = i%3;
		for(j=0;j<4;j++)  //進行空格上下左右移動
		{
			xx = x+dx[j];
			yy = y+dy[j];
			if(xx>=0 && yy>=0 && xx<3 && yy<3)  //如果移動沒有越界
			{
				p = xx*3+yy;
				memcpy(tem2,tem,sizeof(tem));  //將tem拷貝到tem2上面
				tem2[i] = tem2[p];  //交換p和i的值,既移動0的位置
				tem2[p] = 0;
				if(kangtuo(tem2))
				{
					memcpy(q[rear],tem2,sizeof(tem2));
					step[rear] = step[front]+1;
					rear++;
				}
			}
		}
		front++;
	}
	return -1;
}

int main()
{
	memset(state,0,sizeof(state));
	int i,anum;
	string str1,str2;
	cin>>str1;
	cin>>str2;
	for(i=0;i<9;i++)  //將'.'轉換成0
	{
		if(str1[i]>'0'&&str1[i]<='9')
			a[i] = str1[i]-'0';
		else
		{
			a[i] = 0;
			anum = i;
		}
	}
	for(i=0;i<9;i++)
	{
		if(str2[i]>'0'&&str2[i]<='9')
			b[i] = str2[i]-'0';
		else
		{
			b[i] = 0;
			bnum = i;
		}
	}
	int road = bfs();  //根據輸出最後結果
	if(road == -1)
		cout<<road<<endl;
	else
		cout<<step[road]<<endl;
	return 0;
}

最後提一句,重排九宮就是經典bfs演算法中的八數碼,這裡有收藏的八數碼的八種境界,適合新手看一下

http://blog.csdn.net/kopyh/article/details/48442415#t7