1. 程式人生 > >藍橋杯【歷屆試題】九宮重排

藍橋杯【歷屆試題】九宮重排

問題描述:

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


我們把第一個圖的局面記為:12345678.

把第二個圖的局面記為:123.46758

顯然是按從上到下,從左到右的順序記錄數字,空格記為句點。

本題目的任務是已知九宮的初態和終態,求最少經過多少步的移動可以到達。如果無論多少步都無法到達,則輸出-1。

輸入格式

  輸入第一行包含九宮的初態,第二行包含九宮的終態。

輸出格式

  輸出最少的步數,如果不存在方案,則輸出-1。

樣例輸入

12345678.

123.46758

樣例輸出

3

樣例輸入

13524678.

46758123.

樣例輸出

22


題目思路:

我們可以把每一種局面當作是一個結點。空白格可以向四個方向移動,移動後的局面是新的結點。顯然,我們可以通過bfs來搜尋目標局面,並且一旦搜尋到就一定是最少移動次數。我們需要解決的問題有兩個。

1:如何記錄每一種局面。

2:如何判斷當前局面是否已經出現過,即之前的搜尋過程中已經搜尋過了,簡單點說就是判重。

問題1解決方法:

我們可以通過宣告二維陣列,int a[maxstate][9], a[1][9]表示第一種局面,a[2][9]表示第二種局面,這裡我們為了表示方便,typedef int State[9]; State st[maxstate];這樣的話,第i種局面就可以用st[i]表示。

問題2解決方法:

非常直接的方法宣告陣列vis[9][9][9][9][9][9][9][9][9],這樣的一個九維陣列,顯然,這樣做並不是那麼合理的。這裡所用的方法是雜湊對映。

備註:

1:memcmp();memcpy(); memset();

2:我們把3*3的九宮格的數字按照一行一行的從左往右的順序處理成一行資料。假設在這一行資料中他的位置是n,那麼他在二維表中的位置就是(n/3, n%3)。

題目程式碼:

#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
string str1,str2;
typedef int State[9];
int maxstate = 1000000;
State st[1000000] ,goal;
int dist[1000000];
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};

//---
const int hashsize = 1000003;
int head[hashsize], next[hashsize];
int hash(State& s)
{
	int v = 0;
	for(int i=0; i<9; i++) v = v*10 + s[i]; //把9個數字組合成9位數 
	return 	v % hashsize; //確保hash函式值是不超過hash表的大小的非負整數 

}


int try_to_insert(int s)
{
	int h = hash(st[s]); 
	int u = head[h]; //從表頭開始查詢連結串列 
	while(u){
		if(memcmp(st[s], st[u], sizeof(st[s]))==0) return 0; //有重複,插入失敗 
		u = next[u]; //順著連結串列繼續找 
	}
	next[s] = head[h]; //該結點插入到連結串列中 
	head[h] = s;
	return 1;
}

void init_table()
{
	memset(head, 0, sizeof(head)); 
} 

int bfs()
{
	init_table(); //初始化查詢表 
	int front = 1; int rear = 2; 
	while(front < rear){
		State& s = st[front];
		if(memcmp(goal, s, sizeof(s)) == 0) return front;
		int z;
		for(int i=0; i<9; i++) if(!s[i]) z = i; //找“0”的位置 
		int x = z/3, y = z%3;
		for(int i=0; i<4; i++){
			int newx = x + dx[i];
			int newy = y + dy[i];
			int newz = newx * 3 + newy;
			if(newx>=0 && newx<3 && newy>=0 && newy<3){
				State& t = st[rear];
				memcpy(&t ,&s ,sizeof(s)); //擴充套件新結點 
				t[newz] = s[z];
				t[z] = s[newz];
				dist[rear] = dist[front] + 1;
				if(try_to_insert(rear)) rear++; //查重  修改隊尾指標 
			}
		}
		front++; //修改隊首指標 
	}
	
	return 0;
}



int main()
{
	cin>>str1>>str2;
	for(int i=0; i<9; i++) {
		if(str1[i]=='.') 
		st[1][i] = 0;
		else
		st[1][i] = str1[i] - '0';
	}
	for(int i=0; i<9; i++){
		if(str2[i]=='.')
		goal[i] = 0;
		else
		goal[i] = str2[i] - '0';
	} 

	int ans = bfs();
	if(ans == 0) printf("%d\n",-1);
	else 
	printf("%d\n",dist[ans]);
	
	return 0;
}