• 八数码(双向广搜)


    早上看了提到双向广搜的一篇文章,其中讲了双向广搜可以节约一半的时间和一半的空间(理论上),我画了一幅图:

    (上面的对应普通BFS,下面的对应双向广搜)

    可以看出简单BFS的搜索节点大约是双向广搜的二倍。

    对于八数码问题,由于逆序剪枝可以将所有无解的状态全部剪掉,剩余的都是有解的状态,所以使用双向广搜速度可能会更快;

    对下面两组数据(分别输入)

    1 2 3 4 5 6 7 8 0
    8 7 6 5 4 3 2 1 0

    2 6 4 1 3 7 0 5 8
    8 1 5 7 3 6 4 0 2

    正确输出对应是 30、 31

    使用BFS的运行时间:0.390s  0.359s

    使用双广的运行时间:0.109s 0.046s

    双广我开始想按自己的思路来写,定义的是两个队列,两个vis[],两个dist[],最后发现根本运行不了(后来写后面的时才发现是将几个大的数组定义在了局部引起的),睡了一觉,看了这篇Poj 1915 - Knight Moves 双向广搜(Maxwell.O.Y),才对双广的结构有了新的理解;

      1 # include <stdio.h>
      2 # include <mem.h>
      3 
      4 # define MAXN (362880 + 5)
      5 
      6 typedef struct 
      7 {
      8     char a[9];
      9 }state;
     10 
     11 const int dir[4][2] = {{-1,0}, {0,1}, {1,0}, {0,-1}};
     12 int fact[9];
     13 
     14 int front, rear;
     15 state cur, nst;            /* new state */
     16 char vis[MAXN];
     17 char dist[MAXN];        /* 求的是最短距离( < 100),可以用 char 类型 */
     18 state Q[MAXN/2];
     19 
     20 
     21 void read(state *s);
     22 int inversions(state s);
     23 int cantor(state s);
     24 void init_fact(void);
     25 int bfs_d(state start, state goal);
     26 
     27 int main()
     28 {
     29     state start, goal;
     30     
     31     freopen("in.txt", "r", stdin);
     32     freopen("out.txt", "w", stdout);
     33     
     34     init_fact();
     35 
     36     read(&start);
     37     read(&goal);
     38 
     39     if (inversions(start)%2 == inversions(goal)%2)
     40     {
     41         printf("%d\n", bfs_d(start, goal));
     42     }
     43     else puts("-1");
     44 
     45     return 0;
     46 }
     47 
     48 int bfs_d(state start, state goal)
     49 {
     50     int i, x, y, nx, ny, ct, nt;
     51 
     52     memset(vis, 0, sizeof(vis));
     53     memset(dist, 0, sizeof(dist));
     54 
     55     front = 1;
     56     Q[front] = start;
     57     rear = 2;
     58     Q[rear++] = goal;
     59     vis[cantor(start)] = 1;                /* 1 表示从起始节点扩展得到 */
     60     vis[cantor(goal)] = 2;                /* 2 表示从目标节点扩展得到 */
     61 
     62     while (front < rear)
     63     {
     64         cur = Q[front++];
     65         ct = cantor(cur);
     66         for (i = 0; cur.a[i] && i < 9; ++i) ;
     67         x = i / 3;
     68         y = i % 3;
     69         for (i = 0; i < 4; ++i)
     70         {
     71             nx = x + dir[i][0];
     72             ny = y + dir[i][1];
     73             if (nx>=0 && nx<3 && ny>=0 && ny<3)
     74             {
     75                 nst = cur;
     76                 nst.a[x*3+y] = cur.a[nx*3+ny];
     77                 nst.a[nx*3+ny] = 0;
     78                 if (!vis[nt = cantor(nst)])
     79                 {
     80                     Q[rear++] = nst;
     81                     /* foot[nt] = ct; */
     82                     dist[nt] = dist[ct] + 1;
     83                     vis[nt] = vis[ct];
     84                 }
     85                 else if (vis[ct] != vis[nt])
     86                 {
     87                     return 1 + dist[nt] + dist[ct];
     88                 }
     89             }
     90         }
     91     }
     92 
     93     return -1;
     94 }
     95 
     96 void read(state *s)
     97 {
     98     int i;
     99     char c[5];
    100 
    101     for (i = 0; i < 9; ++i)
    102     {
    103         scanf("%s", c);
    104         if (c[0] == 'x') (*s).a[i] = 0;
    105         else (*s).a[i] = c[0] - '0';
    106     }
    107 }
    108 
    109 int inversions(state s)
    110 {
    111     char ch;
    112     int i, j, ret;
    113 
    114     ret = 0;
    115     for (i = 0; i < 9; ++i)
    116     {
    117         if (s.a[i] == 0) continue;
    118         ch = s.a[i];
    119         for (j = i+1; j < 9; ++j)
    120         {
    121             if (s.a[j] < ch && s.a[j] != 0)
    122                 ++ret;
    123         }
    124     }
    125 
    126     return ret;
    127 }
    128 
    129 int cantor(state s)
    130 {
    131     char ch;
    132     int i, j, ret, cnt;
    133 
    134     ret = 0;
    135     for (i = 0; i < 9; ++i)
    136     {
    137         cnt = 0;
    138         ch = s.a[i];
    139         for (j = i+1; j < 9; ++j)
    140         {
    141             if (s.a[j] < ch)
    142                 ++cnt;
    143         }
    144         ret += cnt*fact[8-i];
    145     }
    146 
    147     return ret;
    148 }
    149 
    150 void init_fact(void)
    151 {
    152     int i;
    153 
    154     fact[0] = 1;
    155     for (i = 1; i < 9; ++i)
    156     {
    157         fact[i] = i * fact[i-1];
    158     }
    159 }

    用到的主要有:逆序剪枝,康托展开,双向广搜(……第四层)。

    ********************************************

    刚吃饭想到了一种比较不正常的提速方法,也是看了郭大侠的博客才考试考虑提速的:假设最短距离的最大值是31,相应扩展的节点为N的话,根据BFS的特点完全可以将队列缩小很多!试了试果然提速了不少:(对应上面的例子)15ms 31ms;

    郭大侠提出了一种构造法(非最优解)可以在有限的步数内按照一定的规则构造出一个解,对于大多数输入,速度要比搜索要快一些。

  • 相关阅读:
    url传递参数带 + ,解决办法
    操作系统——内存地址重定位
    算法——二分查找变形题
    Java——代码性能优化
    maven——添加插件和添加依赖有什么区别?
    JavaWeb——Servlet如何调用线程池中的线程?
    「ZJOI2016」小星星
    [十二省联考2019]字符串问题
    [十二省联考2019]春节十二响
    [十二省联考2019]异或粽子
  • 原文地址:https://www.cnblogs.com/JMDWQ/p/2510698.html
Copyright © 2020-2023  润新知