1. 程式人生 > 其它 >八數碼問題簡單解決辦法

八數碼問題簡單解決辦法

問題分析:

八數碼問題是一個經典的BFS問題,把棋局看成一個狀態圖,共有9!種狀態。從初始棋局開始,每次轉移到下個狀態,直到目標棋局為止。
仔細分析可知,八數碼的關鍵是判重,如果不去除重複狀態,程式會產生很多無效狀態,從而複雜度大大增加

解決演算法:

BFS + Cantor

案例分析:

(0表示空格所在位置)
初始棋局:
|1|2|3|
|0|8|4|
|7|6|5|

目標棋局:
|1|0|3|
|8|2|4|
|7|6|5|

1.先將空格和8交換得到:
|1|2|3|
|8|0|4|
|7|6|5|

2.再將空格和2交換得到目標棋局:
|1|0|3|
|8|2|4|
|7|6|5|

總共執行兩次操作

C++程式碼:


#include <bits/stdc++.h>
using namespace std;

const int LEN = 362880;	// 共9!種狀態

struct node
{
	int state[9];	// 記錄八數碼排列,即一個狀態
	int dis;
};

int dir[4][2] = {	//左,上,右,下順時針方向
	{-1,0},
	{0,-1},
	{1,0},
	{0,1},
};

int visited[LEN] = {0};	// cantor判重,若某狀態訪問過置為一
int start[9];
int goal[9];
long factory[] = {1,1,2,6,24,120,720,5040,40320,362880};	// cantor判重用到的常數,從0!到9!

bool cantor(int str[], int n) {
	long result = 0;
	for(int i=0; i<n; ++i) {
		int cnt = 0;
		for(int j=i+1; j<n; ++j)
			if(str[i] > str[j])
				cnt++;
		result += cnt*factory[n-i-1];
	}
	if(!visited[result]) {
		visited[result] = 1;
		return true;
	}
	else return false;
}

int bfs() {
	node head;
	memcpy(head.state, start, sizeof(head.state));
	head.dis = 0;
	queue<node> q;
	cantor(head.state, 9);
	q.push(head);

	while(!q.empty()) {
		head = q.front();
		q.pop();
		int z;
		for(z=0; z<9; ++z)
			if(head.state[z] == 0)	//尋找元素0的位置
				break;

		// z的二維轉換
		int x = z%3;
		int y = z/3;

		// 向四個方向轉移新狀態
		for(int i=0; i<4; ++i) {
			int nx = x + dir[i][0];
			int ny = y + dir[i][1];
			int nz = ny*3 + nx;	// 二維化一維
			if(nx >= 0 && nx <3 && ny >= 0 && ny < 3) {	//未越界
				node nnode;
				memcpy(&nnode, &head, sizeof(struct node));
				swap(nnode.state[z], nnode.state[nz]);
				nnode.dis++;
				if(memcmp(nnode.state, goal, sizeof(goal)) == 0)	//與目標狀態比較
					return nnode.dis;
				if(cantor(nnode.state, 9))	//判重
					q.push(nnode);	//把新的狀態放進佇列
			}
		}
	}
	return -1;	//沒找到
}

int main() {
	//freopen("in.txt", "r", stdin);
	for(int i=0; i<9; ++i)
		cin >> start[i];
	for(int i=0; i<9; ++i)
		cin >> goal[i];
	int num = bfs();
	if(num != -1)
		cout << num << endl;
	else
		cout << "impossible" << endl;
	return 0;
}