zoukankan      html  css  js  c++  java
  • HDU-1043 Eight八数码 搜索问题(bfs+hash 打表 IDA* 等)

    题目链接 https://vjudge.net/problem/HDU-1043


    经典的八数码问题,学过算法的老哥都会拿它练搜索

    题意:

    给出每行一组的数据,每组数据代表3*3的八数码表,要求程序复原为初始状态

    思路:

    参加网站比赛时拿到此题目,因为之前写过八数码问题,心中暗喜,于是写出一套暴力bfs+hash,结果TLE呵呵

    思路一:bfs+hash(TLE)

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <queue>
     4 #include <set>
     5 using namespace std;
     6 const int StMax=800000, HashMax=50000;
     7 struct State{
     8     char map[3][3];
     9     int dis, fx, x, y, id, fa;
    10 }start, st[StMax];
    11 int head[HashMax], mynext[StMax], dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
    12 char ch[4]={'r', 'l', 'd', 'u'};
    13 int myhash(State &a){
    14     a.id=0;
    15     for (int y=0; y<3; y++) 
    16         for (int x=0; x<3; x++) 
    17             a.id=a.id*10+((a.map[y][x]=='x')?'0':a.map[y][x])-'0';
    18     return a.id%HashMax;
    19 }
    20 int insert(int rear){
    21     int h=myhash(st[rear]), u=head[h];
    22     while(u){
    23         if (st[rear].id==st[u].id) return 0;
    24         u=mynext[u];
    25     }
    26     mynext[rear]=head[h]; head[h]=rear;
    27     return 1;
    28 }
    29 void output(int u){
    30     if (u==0) printf("unsolvable");
    31     else if (u==1) return;
    32     else{
    33         output(st[u].fa);
    34         printf("%c", ch[st[u].fx]);
    35     }
    36 }
    37 
    38 int bfs(void){
    39     st[1]=start; insert(1);
    40     if (start.id==123456780) return 1;
    41     int front=1, rear=2;//2,1 for hash
    42     while (front<rear){
    43         State &s=st[front];
    44         for (int i=0; i<4; i++){
    45             int nx=s.x+dir[i][0], ny=s.y+dir[i][1];
    46             
    47             if (nx<0 || nx>=3 || ny<0 || ny>=3) continue;
    48             State &t=st[rear]; memcpy(&t, &s, sizeof(s));
    49             t.map[s.y][s.x]=s.map[ny][nx];
    50             t.map[ny][nx]='x';
    51             if (!insert(rear)) continue; 
    52             t.x=nx; t.y=ny; t.fx=i; t.dis++; t.fa=front;
    53 
    54             if (t.id==123456780) return rear;
    55             rear++;
    56         }front++;
    57     }
    58     return 0;
    59 }
    60 int input(void){
    61     char a[255]; int p=0, re;
    62     if ((re=scanf("%[^
    ]
    ", a))!=1) return 0;
    63     for (int y=0; y<3; y++)
    64         for (int x=0; x<3; x++){
    65             while(a[p]==' ') p++;
    66             if ((start.map[y][x]=a[p])=='x') {start.x=x; start.y=y;}
    67             p++;
    68         }
    69     start.dis=0;
    70     return 1;
    71 }
    72 
    73 int main(void){
    74     while (input()){
    75         memset(head, 0, sizeof(head));
    76         memset(mynext, 0, sizeof(mynext));
    77         output(bfs()); printf("
    ");
    78     }
    79 
    80     return 0;
    81 }

    看来hdu的数据比较强,比较多,考虑到八数码问题状态数不是非常大(<9!=362880<10^6)

    (注:参考紫书 一般情况状态总数小于10^6在可接受范围)
    于是考虑bfs的预处理打表,在此期间了解到康托展开用以编码全排列

    思路二:bfs打表+cantor(AC)

    中间三个数据分别是Time(ms) Mem(MB) Length

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <vector>
     4 using namespace std;
     5 typedef int State[9];
     6 const int STMAX=362880; 
     7 int fact[10]={1,1,2,6,24,120,720,5040,40320,362880}, dir[4][2]={0,-1,-1,0,0,1,1,0};
     8 int st[STMAX][9], vis[STMAX], myprev[STMAX], fx[STMAX], goal=46233, stcode[STMAX];
     9 char toch[4]={'d','r','u','l'};//反方向 
    10 int encode(int map[], int n){
    11     int code=0;
    12     for (int i=0; i<n; i++){
    13         int cnt=0;
    14         for (int j=i+1; j<n; j++) 
    15             if (map[i]>map[j]) cnt++;
    16         code+=cnt*fact[n-1-i];
    17     }return code; 
    18 }
    19 
    20 int input(void){
    21     char ch;
    22     for (int i=0; i<9; i++){
    23         do{if (scanf("%c", &ch)!=1) return 0;}while(ch==' '||ch=='
    ');
    24         if (ch=='x'||ch=='X') ch='0';
    25         st[0][i]=ch-'0';
    26     }
    27     return 1;
    28 }
    29 
    30 int check(void){
    31     int sum=0;
    32     for (int i=0; i<9; i++){
    33         if (st[0][i]==0) continue;
    34         for (int j=i+1; j<9; j++){
    35             if (st[0][j]==0) continue;
    36             if (st[0][i]>st[0][j]) sum++;
    37         }
    38     }
    39     return sum;
    40 }
    41 
    42 void show(vector<char> &path, int code){
    43     if (code==goal) return;
    44     else{
    45         show(path, myprev[code]);
    46         path.push_back(toch[fx[code]]);
    47     }
    48 }
    49 
    50 void pre(void){
    51     memset(vis, 0, sizeof(vis));
    52     memset(myprev, 0, sizeof(myprev));
    53     State s={1,2,3,4,5,6,7,8,0}; memcpy(st[0], &s, sizeof(s));
    54     vis[stcode[0]=encode(st[0], 9)]=1;
    55     int front=0, rear=1;
    56     while (front<rear){
    57         State &a=st[front];
    58         
    59         int z=0; while (a[z]) z++;
    60         for (int i=0; i<4; i++){
    61             int nx=z%3+dir[i][0], ny=z/3+dir[i][1];
    62             if (nx<0 || nx>2 || ny<0 || ny>2) continue;
    63             State &b=st[rear]; memcpy(&b, &a, sizeof(a));
    64             b[nx+ny*3]=0; b[z]=a[nx+ny*3];
    65             
    66             int code=encode(b, 9);
    67             if (vis[code]) continue;
    68             fx[code]=i; myprev[code]=stcode[front];
    69             stcode[rear]=code; vis[code]=1; rear++;
    70         }front++;
    71     }
    72 }
    73 
    74 int main(void){
    75     pre();
    76     while (input()){
    77         vector<char> path;
    78         int code=encode(st[0], 9);
    79         if (!vis[code]) printf("unsolvable
    ");
    80         else {
    81             show(path, code);
    82             for (int i=path.size()-1; i>=0; i--)
    83                 printf("%c", path[i]);
    84             printf("
    ");
    85         }
    86     }
    87     
    88     return 0;
    89 }

    解题到此结束,但在此期间想到过新学的IDA*,按结果来说也是不错的

    思路三:IDA*(AC)


    (没错,我特地重新上传了一次,因为之前的代码有不少啰嗦的地方)

    我觉得此题用作IDA*的入门题目非常合适,dfs()中排除上次操作的反方向(prevDir)是一个很实用的小技巧,排除了许多分支

     1 #include <cstdio>
     2 #include <cmath>
     3 #include <cstring>
     4 #include <vector>
     5 using namespace std;
     6 typedef int State[9];
     7 State st, goal={1,2,3,4,5,6,7,8,0};
     8 int maxd;
     9 int isdir[4]={2,3,0,1}, orix[9]={2,0,1,2,0,1,2,0,1}, oriy[9]={2,0,0,0,1,1,1,2,2}, dir[4][2]={0,-1,-1,0,0,1,1,0};
    10 char toch[4]={'u', 'l', 'd', 'r'};
    11 int input(void){
    12     char ch;
    13     for (int i=0; i<9; i++){
    14         do{if(scanf("%c", &ch)!=1) return 0;}while (ch==' '||ch=='
    ');
    15         if (ch=='x') ch='0';
    16         st[i]=ch-'0';
    17     }
    18     return 1;
    19 }
    20 
    21 int check(void){
    22     int sum=0;
    23     for (int i=0; i<9; i++){
    24         if (st[i]==0) continue;
    25         for (int j=i+1; j<9; j++){
    26             if (st[j]==0) continue;
    27             if (st[i]>st[j]) sum++;
    28         }
    29     }
    30     return sum;
    31 }
    32 inline int calc(State &a){
    33     int sum=0;
    34     for (int i=0; i<9; i++)
    35         sum+=abs(i%3-orix[st[i]])+abs(i/3-oriy[st[i]]);
    36     return sum;
    37 }
    38 
    39 int dfs(State &a, vector<char> &path, int z, int prevdir, int d){
    40     int h=calc(a);
    41     if (h==0) return 1;
    42     if (maxd==d) return 0;
    43     
    44     if (h>1*(maxd-d)) return 0;
    45     for (int i=0; i<4; i++){
    46         if (prevdir!=-1 && isdir[prevdir]==i) continue;//great effect
    47         int nx=z%3+dir[i][0], ny=z/3+dir[i][1];
    48         if (nx<0 || nx>2 || ny<0 || ny>2) continue;
    49         a[z]=a[nx+ny*3]; a[nx+ny*3]=0; path.push_back(toch[i]);
    50         if (dfs(a, path, nx+ny*3, i, d+1)) return 1;
    51         a[nx+ny*3]=a[z]; a[z]=0; path.pop_back();
    52     }return 0;
    53 }
    54 
    55 int main(void){
    56     while (input()){
    57         if (check()%2) {printf("unsolvable
    "); continue;}
    58         int z=0; while(st[z]) z++;
    59         for (maxd=0; ; maxd++){
    60             vector<char> path;
    61             if (dfs(st, path, z, -1, 0)){
    62                 for (int i=0; i<path.size(); i++) printf("%c", path[i]);
    63                 printf("
    ");
    64                 break;
    65             }
    66         }
    67     }
    68     return 0;
    69 }

    其他思路:

    双向BFS:

    若需要路径,则一定需判断节点是否由另一队列走过,并链接两队列中的路径(考虑cantor)
    A*+cantor:

    使用priority_queue(优先队列),启发函数类似IDA*

    (实际情况下我比较喜欢IDA*,因为它比较短,也好找错。。。)

  • 相关阅读:
    js 遍历数组对象求和
    小程序使用微信地址or小程序跳转设置页
    css内容渐入效果实现
    flutter实现文字超出最大宽度显示省略号
    flutter查看安全码SHA1
    Uncaught (in promise)
    小程序iphone蒙层滚动穿透
    map中使用箭头函数遇到的坑
    骨架屏css样式
    javascript(js)反转字符串
  • 原文地址:https://www.cnblogs.com/tanglizi/p/7420430.html
Copyright © 2011-2022 走看看