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问题,可以用“康托展开”判重。那什么事康托展开呢?
康托展开是一种特殊的哈希函数,针对八数码问题,康托展开完成了如表所示的工作。
状态0123456780123456870123456768......876543210
Cantor012......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] != '\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 }

 


转载于:https://www.cnblogs.com/Tangent-1231/p/11252626.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值