C++編寫解決八數碼問題
阿新 • • 發佈:2021-01-04
**C++編寫解決八數碼問題##
一、需求與規則說明
八數碼問題又稱重排九宮問題,在一個 33 的棋盤上,隨機放置 1 到 8 的數
字棋子,剩下一個空位,如圖所示。數字可以移動到空位(程式設計時,空位可用 0
代替,且可以理解為是空位的上、下、左、右移動),經過若干次移動後,棋局
到達指定目標狀態。
說明:重排九宮問題,對任意給定初始狀態,可達下圖所示兩個目標之一,
不可互換。
目標一:如下圖 G
目標二:如下圖 G1 或 G2
要求:
① 程式設計求解問題;
② 給出中間狀態;
③ 給出解序列(函式呼叫序列)
二、設計
廣搜遍歷:
(1)使用者輸入八數碼的初始狀態後,將狀態壓縮為一個整數。
(3)首先判斷資料是否已經是目標狀態,若是,則結束函式。
(4)若不是,系統將資料轉化為二維陣列,並記錄八數碼中0在陣列中的位置,然後在陣列中依次向四個方向擴充套件。若越界則直接進行下個方向的擴充套件,其餘繼續擴充套件。
(5)擴充套件過程中將0與擴充套件方向的資料交換,轉化為整數並判斷此資料在全排列中的位置是否記為已訪問,若未訪問過,將此資料記錄入結構體陣列中,並判斷該資料是否為目標狀態。否則跳出此次擴充套件,最後陣列交換為擴充套件前的陣列,繼續向其他方向擴充套件。
(6)該資料的四個方向擴充套件完成後,繼續擴充套件結構體陣列中下一個資料,重複(4)(5)過程,直至達到目標狀態或不能繼續擴充套件的狀態。
三、原始碼
注意:需要用到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演算法,啟發式演算法,個人學習程式設計時間不長,在看了許多博主的發表後,感覺啟發式演算法像是在廣搜的基礎上加了一些判斷條件,試著寫了,確實減少的遍歷的路徑,但不太肯定,希望有人可以指點。
個人學習時間不長,如有錯誤和改進,請一定要提出來,謝謝