• 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*,因为它比较短,也好找错。。。)

  • 相关阅读:
    Atitit 模板引擎总结 v4 saa 目录 1. 模板引擎 1 1.1. 构成渲染引擎+指令系统 1 1.2. 模板语法mustache语法 es6 el语法 1 2. 常见模板步骤 1 2.
    Atitit 常见项目角色与职责 目录 1.1. 常见项目角色与职责 1 1.2. 解决问题思路:一般百度,问同事,问上一级 1 1.3. 解决问题时限:与跳过法 1 1.4. 解决方法,一般实
    Atitit 法学处罚方式模式 目录 1. 申诫罚、财产罚和能力罚 1 1.1. 申诫罚 (警告和通报批评 ) 1 1.2. 财产罚是指使被处罚人的财产权利和利益受到损害的行政处罚。 2 1.2
    java DefaultMutableTreeNode 树形结构 目录 1. Tree的概念 1 1.1. treeNode接口,mutabletreenode接口 1 1.2. 104:以T
    Atitit it软件领域职称评级规定,广博方向。 目录 1. 软件工程师资格证 1 1.1. 法规规范 十大标准,三级五晋制。 1 1.2. 组织架构 域职称评级委员会 2 1.3. 人员职责流程表
    Atitit 学校工作手册attilax艾提拉总结 目录 1. 团队文化 宗旨 与使命 2 1.1. 学术教育vs 技术教育vs 技能职业教育 2 1.2. 天堂模式vs地狱模式 2 2. 组织结构
    Java项目部署目录结构与部署方法 打包方法attilax总结 目录 1.1. Java web项目部署目录结构 1 2. Springboot项目的部署结构 2 3. Java项目的开发模式下目录
    Atitit 人员级别评定法 目录 1.1. 十级评定法110 vs 年级评定法 1 1.2. 工龄评定 职级 岗位级别 1 2. 修订系数 学历*授课+绩效 1 3. 计算方法 1
    Atitit java webservice客户端v2 目录 1.1. 生成stub代码wsimport.exe 1 1.2. Wsimport 2 1.3. clienttest 3 1.1
    Atitti 类库 开发者 常用 api 目录 1.1. Meta anno注解元数据api 1 1.2. Ref 反射api 1 1.3. Database meta api 1 1.4.
  • 原文地址:https://www.cnblogs.com/tanglizi/p/7420430.html
Copyright © 2020-2023  润新知