日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

采用状态空间法求解八数码问题

發布時間:2023/12/31 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 采用状态空间法求解八数码问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

采用狀態空間法求解八數碼問題

  • 1. 問題描述
  • 2. 算法思路
    • 2.1 判斷是否有解
    • 2.2 廣度優先搜索求解
    • 2.3 去重
    • 2.4 輸出解路線
  • 3. 數據結構及函數說明
    • 3.1 數據結構
    • 3.2 函數說明
      • 3.2.1 input
      • 3.2.2 haveSolution
      • 3.2.3 deCode
      • 3.2.4 explore
      • 3.2.5 getSolution
      • 3.2.6 print
  • 4. 流程圖
  • 5. C++代碼
  • 6. 代碼運行結果

1. 問題描述

??八數碼難題也稱九宮問題,它是在3×3的方格棋盤上,分別放置了表有數字1、2、3、4、5、6、7、8的八張牌,初始狀態S0S_0S0?,目標狀態SgS_gSg?,要求程序能輸入任意的初始狀態和目標狀態,通過空格來移動八張牌使得棋盤由初始狀態到達目標狀態。移動規則為:每次只能將與空格(上下左右)相鄰的一個數字平移到空格中。

2. 算法思路

2.1 判斷是否有解

??給定任意符合條件的初始狀態S0S_0S0?和目標狀態SgS_gSg?,并不一定存在解路線使得將初始狀態轉化為目標狀態。因此,在求解前需要判斷是否有解。
??八數碼問題的一個狀態實際上是0~9的一個排列,空格用0表示,對于任意給定的初始狀態和目標狀態,不一定有解,也就是說從初始狀態不一定能到達目標狀態。因為排列有奇排列和偶排列兩類,排列只能在同類排列之間轉化,而從奇排列不能轉化成偶排列或相反。
??如果一個數字0~8的隨機排列871526340,用F(X)F(X)F(X)表示數字XXX前面比它小的數的個數,全部數字的F(X)F(X)F(X)之和為Y=∑(F(X))Y=\sum(F(X))Y=(F(X)),如果YYY為奇數則稱原數字的排列是奇排列,如果YYY為偶數則稱原數字的排列是偶排列。
??例如:871526340這個排列的Y=0+0+0+1+1+3+2+3+0=10,10是偶數,所以是偶排列。871625340,Y=0+0+0+1+1+2+2+3+0=9,9是奇數,所以是奇排列。
??因此,可以在運行程序前檢查初始狀態和目標狀態的排列是否相同,相同則問題可解,接著采用搜索算法求解,否則無解。

2.2 廣度優先搜索求解

??我們定義結點nodenodenode ,搜索中每次出現的一個棋盤都相當于一個結點nodenodenode,我們定義一個隊列queuequeuequeue用于保存搜索中所出現的所有棋盤結點,一個棋盤結點為隊列的一個成員。
??廣度優先搜索的基本思想是:
??從初始結點hhh開始, 逐層地對結點進行擴展并考察它是否為目標結點,若不是目標結點,則放入待考察隊列中;在第 n 層的結點沒有全部擴展并考察之前,不對第 n+1 層的結點進行擴展。隊列中的結點總是按進入的先后順序排列,先進入的結點排在前面,后進入的排在后面。其搜索過程如圖1所示。

圖1 廣度優先搜索過程

2.3 去重

??在搜索過程中,可能會同一狀態重復搜索,有很多重復狀態節點的擴展,會浪費時間。為解決這一問題使用哈希mapmapmapmapmapmap鍵值為已經擴展過的狀態節點編碼后的字符串。當需要擴展狀態節點iii時,若iii已經在mapmapmap中,即已經擴展過,就不再擴展,否則對其進行擴展,并將iii加入到mapmapmap中。這里的哈希mapmapmap使用C++ STL中的unordered_mapunordered\_mapunordered_map實現。

2.4 輸出解路線

??每個節點保存其父節點,該節點由其父節點擴展而來,當搜索到目標狀態時,根據父節點信息回溯回去,得到由初始狀態到目標狀態的解路線。

3. 數據結構及函數說明

3.1 數據結構

struct node {int a[3][3]; //九宮格 node* p; //指向當前節點的父節點 node* next; //指向隊列中下一個節點 node(string s) {for(int i=0;i<len;i++) {for(int j=0;j<len;j++) a[i][j] = s[i*len+j]-'0';}p = NULL;next = NULL;}node():p(NULL), next(NULL) {} };

??定義一個結構體node表示廣度優先搜索中的一個狀態節點,其中:a是3*3二維數組,用來表示九宮格中每個位置的數字;p是指針,用來指向當前節點的父節點,這里的父節點是指當前節點由其父節點擴展而來;next是指針,指向廣度搜索算法隊列中的下一個節點;兩個構造函數,分別用于有參數和無參數時結構體的初始化。

3.2 函數說明

3.2.1 input

??void input(node& start, node& target),鍵盤輸入八數碼初始狀態和目標狀態。

3.2.2 haveSolution

??bool haveSolution(node start, node target),判斷輸入的初始狀態和目標狀態是否有解,若有解返回true,否則返回false。

3.2.3 deCode

??string deCode(node t),將node t中的八數碼進行編碼變成字符串,并返回編碼后的字符串。

3.2.4 explore

??bool explore(node cur, node& ex, char dic),該函數實現對cur狀態節點進行指定方向的擴展,并通過ex參數將擴展后的結果返回,dic取值有“u,d,l,r”,分別表示空格向上、下、左、右移。若擴展成功返回true,否則返回false。

3.2.5 getSolution

??node* getSolution(node* start, node* target),輸入參數為指向初始狀態節點的指針和指向目標狀態節點的指針;該函數計算出一條從初始狀態轉化為目標狀態的一條路線,并返回指向該路線最后一個節點的指針。

3.2.6 print

??void print(node* t),輸入參數t即為getSolution得到的指向結果路線最后一個節點的指針,print函數輸出完整路線。

4. 流程圖

??程序總體流程圖如圖2所示。

圖2 程序總體流程圖

5. C++代碼

#include <bits/stdc++.h> #include <string> #define len 3 using namespace std;struct node {int a[3][3]; //九宮格 node* p; //指向當前節點的父節點 node* next; //指向隊列中下一個節點 node(string s) {for(int i=0;i<len;i++) {for(int j=0;j<len;j++) a[i][j] = s[i*len+j]-'0';}p = NULL;next = NULL;}node():p(NULL), next(NULL) {} };void input(node& start, node& target); bool haveSolution(node start, node target); string deCode(node t); bool explore(node cur, node& ex, char dic); node* getSolution(node* start, node* target); void print(node* t);int main() {node start, target;input(start, target);if(!haveSolution(start, target)) {cout<<"奇偶不同,無解!!!"<<endl;return 0;}node* t = getSolution(&start, &target);if(t == NULL) cout<<"無解!!!"<<endl;else print(t);system("pause");return 0; } void input(node& start, node& target) {//輸入任意合法的初始狀態和目標狀態cout<<"請輸入八數碼初始狀態,以3*3矩陣形式輸入,空格用0表示"<<endl; for(int i=0;i<len;i++) {for(int j=0;j<len;j++) cin>>start.a[i][j];}cout<<"請輸入八數碼目標狀態,要求同初始狀態"<<endl; for(int i=0;i<len;i++) {for(int j=0;j<len;j++) cin>>target.a[i][j];} }bool haveSolution(node start, node target) {//判斷初始狀態和目標狀態是否同為奇排列或同為偶排列,即是否有解 string s = "", t = "";int sc = 0, tc = 0;for(int i=0;i<len;i++) {for(int j=0;j<len;j++) {s += start.a[i][j]+'0';t += target.a[i][j]+'0';}}for(int i=1;i<s.length();i++) {for(int j=0;j<i;j++) {if(s[j]<s[i] && s[i]!='0' && s[j]!='0') sc++;if(t[j]<t[i] && t[i]!='0' && t[j]!='0') tc++;}}if(sc%2 == tc%2) return true;else return false; }string deCode(node t) {//將九宮格中的數字編碼為字符串 string r = "";for(int i=0;i<len;i++) {for(int j=0;j<len;j++) r += t.a[i][j]+'0';}return r; }bool explore(node cur, node& ex, char dic) {//對當前狀態cur進行擴展,dic為'u'表示空格向上移,'d'表示空格向下移//'l'表示向左移,'r'表示向右移,若無法擴展,返回false,若擴展成功,返回true,//并通過ex參數返回擴展后的節點 int r = 0, c = 0;for(int i=0;i<len;i++) {for(int j=0;j<len;j++) {if(cur.a[i][j] == 0) {r = i;c = j;break;}}}if(dic == 'u') {if(r == 0) return false;else swap(ex.a[r][c], ex.a[r-1][c]);}else if(dic == 'd') {if(r == len-1) return false;else swap(ex.a[r][c], ex.a[r+1][c]);}else if(dic == 'l') {if(c == 0) return false;else swap(ex.a[r][c], ex.a[r][c-1]);}else {if(c == len-1) return false;else swap(ex.a[r][c], ex.a[r][c+1]);}return true; }node* getSolution(node* start, node* target) {unordered_map<string,bool> m;node *front = start, *rear = front;string tarCode = deCode(*target);while(front!=NULL) {string curCode = deCode(*front);if(curCode == tarCode) {//找到目標狀態 return front;}else {if(m.find(curCode)==m.end()) {//當前狀態還未擴展過,進行擴展m[curCode] = true;node* dic[4];char op[4] = {'u','d','l','r'};for(int i=0;i<4;i++) dic[i] = new node(curCode);for(int i=0;i<4;i++) {if(explore(*front, *dic[i], op[i])) {dic[i]->p = front;rear->next = dic[i];rear = rear->next;}}}}front = front->next;}return NULL; }void print(node* t) {stack<node> s;while(t!=NULL) {s.push(*t);t = t->p;}cout<<endl<<"轉化路線"<<endl;while(!s.empty()) {node cur = s.top();s.pop();for(int i=0;i<len;i++) {for(int j=0;j<len;j++) cout<<cur.a[i][j]<<" ";cout<<endl;}cout<<endl;} }

6. 代碼運行結果

??代碼運行結果如圖3所示。

圖3 代碼運行結果

總結

以上是生活随笔為你收集整理的采用状态空间法求解八数码问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。