POJ 1077 Eight (BFS+康託展開)詳解

本題知識點和基本代碼來自《算法競賽 入門到進階》(做者:羅勇軍 郭衛斌)node

若有問題歡迎巨巨們提出ios

題意:八數碼問題是在一個3*3的棋盤上放置編號爲1~8的方塊,其中有一塊爲控制,與空格相鄰的數字方塊能夠移動到空格里。咱們要求指定初始棋盤和目標棋盤,計算出最少移動次數,同時要輸出數碼的移動數列。初始棋盤樣例已給出,目標棋盤爲「1 2 3 4 5 6 7 8 x」算法

 

輸入:函數

 2  3  4  1  5  x  7  6  8 

輸出:優化

ullddrurdllurdruldr

詳解:
八數碼是經典的BFS問題,能夠用「康託展開」判重。那什麼事康託展開呢?
康託展開是一種特殊的哈希函數,針對八數碼問題,康託展開完成了如表所示的工做。
狀態 012345678 012345687 0123456768 ...... 876543210
Cantor 0 1 2 ...... 362880-1

函數Cantor()實現的功能是:輸入一個排序,即第一行的某個排序,計算它的Cantor值,即第二行的數。Cantor的時間複雜度爲O(n*n),n是集合中元素的個數,利用CANTOR展開能夠實現八數碼的快速判重。
距離康託展開的實現原理:
例:判斷2143是{1,2,3,4}的全排列中第幾大的數。
計算排在2143前面的排列數目,能夠轉換成如下排列的和:
(1)首位小於2的全部排序,比2小的只有一個數,後面三個數的排序有3!個。
(2)首位爲2,第2位小於1的全部排序,無,寫成0*2!=0.
(3)前兩位爲21,第三位小於4的數,即2134,寫成1*1!=1.
(4)前三位爲214,第四位小於3的數,無,即0*0!=1.
sum=8.即2143是第八大的數。

  把一個集合產生的全排列按字典序排序,第X個排序的計算公式以下:
  X=a[n]*(n-1)!+a[n-1]*(n-2)!+....+a[i]*(i-1)!+...+a[2]*1!+a[1]*0![1].其中,a[i]爲當前未出現的元素排在第幾個。(從0開始)0<=a[i]<i.

康託展開的基礎代碼:
int visited[maxn] = { 0 };  //判斷改裝備是否被訪問過
long int factory[] = { 1,1,2,6,24,120,720,5040,40320,362880 };//階乘數

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

這道題看了不少博客,存步驟的答案方式不少,我是在結構體裏設置string,而後在bfs過程當中逐步保存步驟,最後輸出達到最終狀態的答案。看代碼應該能理解。還有保存圖的時候要注意,樣例裏空格不止一個,因此靈活點保存。我最後時間跑出來是750ms,比較慢,可用其餘搜索方法優化。spa

AC代碼:code

  1 #pragma comment(linker, "/STACK:102400000,102400000")
  2 #pragma GCC optimize(2)
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<cstdio>
  6 #include<cstring>
  7 #include<cmath>
  8 #include<queue>
  9 #include<set>
 10 #include<string>
 11 #include<map>
 12 #include<vector>
 13 #include<ctime>
 14 #include<stack>
 15 using namespace std;
 16 #define mm(a,b) memset(a,b,sizeof(a))
 17 typedef long long ll;
 18 const int maxn = 362880;
 19 const int inf = 0x3f3f3f3f;
 20 
 21 struct node
 22 {
 23     int state[9];
 24     int dis;
 25     string ans;
 26 };
 27 
 28 int dir[4][2] = { {-1,0},{0,-1},{1,0},{0,1} };
 29 char turn[4] = { 'l','u','r','d' };
 30 int visited[maxn] = { 0 };
 31 int start[9];
 32 int goal[9] = {1,2,3,4,5,6,7,8,0};
 33 
 34 long int factory[] = { 1,1,2,6,24,120,720,5040,40320,362880 };
 35 
 36 bool Cantor(int str[], int n)
 37 {
 38     long result = 0;
 39     for (int i = 0; i < n; i++)
 40     {
 41         int counted = 0;
 42         for (int j = i + 1; j < n; j++)
 43         {
 44             if (str[i] > str[j])
 45                 ++counted;
 46         }
 47         result += counted * factory[n - i - 1];
 48     }
 49     if (!visited[result])
 50     {
 51         visited[result] = 1;
 52         return 1;
 53     }
 54     else return 0;
 55 }
 56 
 57 bool check(int x, int y)
 58 {
 59     if (x >= 0 && x < 3 && y >= 0 && y < 3)
 60         return true;
 61     else return false;
 62 }
 63 
 64 queue<char>ans;
 65 
 66 int bfs()
 67 {
 68     node head;
 69     memcpy(head.state, start, sizeof(head.state));
 70     head.dis = 0;
 71     queue<node>q;
 72     Cantor(head.state, 9);
 73     q.push(head);
 74     while (!q.empty())
 75     {
 76         head = q.front();
 77         q.pop();
 78         int z;
 79         for (z = 0; z < 9; z++)
 80         {
 81             if (head.state[z] == 0)
 82                 break;
 83         }
 84         int x = z % 3;
 85         int y = z / 3;
 86         for (int i = 0; i < 4; i++)
 87         {
 88             int newx = x + dir[i][0];
 89             int newy = y + dir[i][1];
 90             int nz = newx + 3 * newy;
 91             if (check(newx, newy))
 92             {
 93                 node newnode = head;
 94                 swap(newnode.state[z], newnode.state[nz]); //0的交換
 95                 newnode.dis++;
 96                 if (memcmp(newnode.state, goal, sizeof(goal)) == 0)
 97                 {
 98                     newnode.ans = newnode.ans + turn[i];
 99                     cout << newnode.ans << endl;
100                     return newnode.dis;
101                 }
102                 if (Cantor(newnode.state, 9))
103                 {
104                     newnode.ans = head.ans + turn[i];
105                     q.push(newnode);
106                 }
107             }
108         }
109     }
110     return -1;
111 }
112 
113 int main()
114 {
115     char s[100];
116     cin.getline(s, 100);
117     int pos = 0;
118     for (int i = 0; s[i] != '\0'; i++)
119     {
120         if (s[i] == ' ') continue;
121         else if (s[i] == 'x') start[pos++] = 0;
122         else start[pos++] = s[i] - '0';
123     }
124     int num = bfs();
125     //printf("%d\n", num);
126     if (num == -1) printf("unsolvable\n");
127     return 0;
128 }
相關文章
相關標籤/搜索