zoukankan      html  css  js  c++  java
  • POJ 1077 Eight (BFS+康托展开)详解

    本题知识点和基本代码来自《算法竞赛 入门到进阶》(作者:罗勇军 郭卫斌)

    如有问题欢迎巨巨们提出

    题意:八数码问题是在一个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,比较慢,可用其他搜索方法优化。

    AC代码:

      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] != ''; 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
    ", num);
    126     if (num == -1) printf("unsolvable
    ");
    127     return 0;
    128 }

  • 相关阅读:
    最短路问题之Dijkstra算法
    最短路问题之Bellman-ford算法
    最小生成树之Kruskal(克鲁斯卡尔)算法
    二分图问题
    七桥问题与欧拉道路
    拓扑排序
    八连通块
    四连通检测
    USACO19DEC题解
    1206 雅礼集训D2题解
  • 原文地址:https://www.cnblogs.com/Tangent-1231/p/11252626.html
Copyright © 2011-2022 走看看