1. 程式人生 > 其它 >C++編寫解決八數碼問題

C++編寫解決八數碼問題

技術標籤:c++廣搜

**C++編寫解決八數碼問題##

一、需求與規則說明

八數碼問題又稱重排九宮問題,在一個 33 的棋盤上,隨機放置 1 到 8 的數
字棋子,剩下一個空位,如圖所示。數字可以移動到空位(程式設計時,空位可用 0
代替,且可以理解為是空位的上、下、左、右移動),經過若干次移動後,棋局
到達指定目標狀態。

說明:重排九宮問題,對任意給定初始狀態,可達下圖所示兩個目標之一,
不可互換。
目標一:如下圖 G
在這裡插入圖片描述
目標二:如下圖 G1 或 G2
在這裡插入圖片描述
要求:
① 程式設計求解問題;
② 給出中間狀態;
③ 給出解序列(函式呼叫序列)

二、設計

廣搜遍歷:
(1)使用者輸入八數碼的初始狀態後,將狀態壓縮為一個整數。

(2)系統將此資料在全排列中的位置記為被訪問(用於判重),同時將次資料儲存入結構體陣列中。
(3)首先判斷資料是否已經是目標狀態,若是,則結束函式。
(4)若不是,系統將資料轉化為二維陣列,並記錄八數碼中0在陣列中的位置,然後在陣列中依次向四個方向擴充套件。若越界則直接進行下個方向的擴充套件,其餘繼續擴充套件。
(5)擴充套件過程中將0與擴充套件方向的資料交換,轉化為整數並判斷此資料在全排列中的位置是否記為已訪問,若未訪問過,將此資料記錄入結構體陣列中,並判斷該資料是否為目標狀態。否則跳出此次擴充套件,最後陣列交換為擴充套件前的陣列,繼續向其他方向擴充套件。
(6)該資料的四個方向擴充套件完成後,繼續擴充套件結構體陣列中下一個資料,重複(4)(5)過程,直至達到目標狀態或不能繼續擴充套件的狀態。
(7)若可以達到目標狀態,則通過子節點遞迴尋找父節點,矩陣輸出每個節點所儲存的資料,若不能則結束
在這裡插入圖片描述

三、原始碼

注意:需要用到easyx資料庫,可以在
https://blog.csdn.net/edc370/article/details/79944550?utm_source=app
下載,博主講的很詳細的

#include<iostream>      //C++標準庫
#include<graphics.h>    //圖形彙編
//#include <windows.h>    //windows視窗控制
using namespace std;

struct Q {
	int
val; int father; }que[110000]; //bfs所用佇列 bool flag[362880] = { false }; //判重 //康託展開判重 int Cantor(int num) { int jc[9] = { 1,1,2,6,24,120,720,5040,40320 }; //分別記錄0~8的階乘 int ans = 1; //記錄全排列的位置,從1開始 char str[9]; //將原資料陣列轉化為字元陣列 for (int i = 8; i >= 0; i--) { str[i] = num % 10; num /= 10; } for (int i = 0; i < 9; i++) { int max = 0; for (int j = i + 1; j < 9; j++) { if (str[i] > str[j]) { max++; } } ans += max * jc[8 - i]; } return ans; } int start, aim; int dx[5] = { 0,0,1,0,-1 }; int dy[5] = { 0,1,0,-1,0 }; int bfs() //廣搜遍歷 { flag[Cantor(start)] = true; que[1].val = start; int head = 0, tail = 1; int now = start; //當前狀態 if (start == 123456780 || start == 123804765 || start == 123456780) { aim = start; return tail; } while (head < tail) { int zero_x, zero_y; //0 的位置 head++; now = que[head].val; int a[5][5] = { 0 }; //將狀態轉化成二維陣列 for (int i = 3; i >= 1; i--) for (int j = 3; j >= 1; j--) { a[i][j] = now % 10; now /= 10; if (a[i][j] == 0) zero_x = i, zero_y = j; } for (int i = 1; i <= 4; i++) { if (a[zero_x + dx[i]][zero_y + dy[i]]) { aim = 0; int x = zero_x + dx[i]; int y = zero_y + dy[i]; swap(a[x][y], a[zero_x][zero_y]); for (int i = 1; i <= 3; i++) for (int j = 1; j <= 3; j++) aim = aim * 10 + a[i][j]; if (!flag[Cantor(aim)]) { que[++tail].val = aim; que[tail].father = head; flag[Cantor(aim)] = 1; } swap(a[x][y], a[zero_x][zero_y]); if (aim == 123456780 || aim == 123804765 || aim == 12345678) //達成目標狀態 { return tail; } } } } cout << "不能達成目標" << endl; return -1; } void dfs(int now) //遞迴輸出解 { if (now == 0) { return; } dfs(que[now].father); int a[5][5]; int num = que[now].val; for (int i = 3; i >= 1; i--) for (int j = 3; j >= 1; j--) { a[i][j] = num % 10; num /= 10; } for (int i = 1; i <= 3; i++) { for (int j = 1; j <= 3; j++) cout << a[i][j] << ' '; cout << endl; } cout << "**************" << endl; } void show(int now) //與dfs函式基本相同 用於演示過程 { static int temp = 0; if (temp == 0) { initgraph(300, 300); //繪製介面 setbkcolor(WHITE); cleardevice(); setlinecolor(BLACK); settextcolor(BLACK); setbkmode(TRANSPARENT); settextstyle(40, 20, "宋體"); temp++; } if (now == 0) { return; } show(que[now].father); int num = que[now].val; int arr[3][3]; for (int i = 2; i >= 0; i--) for (int j = 2; j >= 0; j--) { arr[i][j] = num % 10 + 48; num /= 10; } for (int i = 0; i < 4; i++) { line(100 * i, 0, 100 * i, 300); line(0, 100 * i, 300, 100 * i); } for (int i = 0; i <= 2; i++) for (int j = 0; j <= 2; j++) outtextxy(100 * j + 35, 100 * i + 30, char(arr[i][j])); Sleep(1000); cleardevice(); if (que[now].val == 123456780 || que[now].val == 123804765 || que[now].val == 12345678) { closegraph(); } } int main() { cout << "請輸入八數碼矩陣:" << endl; int t; //輸入初始狀態並進行狀態壓縮 for (int i = 1; i <= 9; i++) { cin >> t; start *= 10; start += t; } int end = bfs(); if (end == -1) { return 0; } //演示部分 cout << "\n" << "即將開始演示" << endl; system("pause"); show(end); Sleep(500); //演示部分結束 cout << "\n" << "演示完畢,按任意鍵檢視全過程" << endl; system("pause"); system("cls"); cout << "**************" << endl; if (aim == 123804765) cout << "該初始狀態可達成目標一" << endl; else cout << "該初始狀態可達成目標二" << endl; cout << "**************" << endl; dfs(end); system("pause"); return 0; }

總結

學習廣度優先搜搜索,八數碼問題是一個很好的案例,可以很好地掌握廣搜,同時,也可以學習深度優先搜尋,與廣搜相類似。
提示:在學會以上的基礎上,可以學習Astar演算法,啟發式演算法,個人學習程式設計時間不長,在看了許多博主的發表後,感覺啟發式演算法像是在廣搜的基礎上加了一些判斷條件,試著寫了,確實減少的遍歷的路徑,但不太肯定,希望有人可以指點。
個人學習時間不長,如有錯誤和改進,請一定要提出來,謝謝