神秘的迷宫

    xiaoxiao2021-03-25  133

    神秘的迷宫

    Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
    Total Submission(s) : 72   Accepted Submission(s) : 18

    Font: Times New Roman | Verdana | Georgia

    Font Size:  

    Problem Description

    ZZC学长因为发现了奇异的花朵被神秘组织绑架到了一个阴暗的迷宫,这个迷宫有四种暗门和最多一个出口,每个暗门都有一把钥匙与其对应,粗心的神秘组织成员把一些钥匙散落在迷宫内。 ZZC学长只有找到钥匙才能打开暗门,他醒来后找到一张也是粗心的神秘组织成员留下的地图。 因为刚刚醒来,ZZC学长一分钟之内只能向上下左右走一格,走路的同时,他也能拿起钥匙或者打开暗门,不会影响走路速度。 ZZC学长希望以最快的速度离开迷宫,聪明的同学能帮帮他么?

    Input

    多组输入,每组输入第一行两个数字N和M表示迷宫的行数和列数。之后N行,每行M个字符描述该迷宫:.表示可以行走的路,#表示出口,*表示迷宫的墙壁,0表示ZZC学长当前位置, 1、2、3、4分别表示每种暗门,5、6、7、8依次对应每种钥匙。(0 < N,M < 1000)

    Output

    对于每组输入,在一行内输出一个数字,表示离开迷宫的最短时间,若无法找到出口,则输出-1。

    Sample Input

    3 3 ... .0. .#. 5 5 *.0.* .1*5* .**.* ...** *#***

    Sample Output

    1 11 hint:第二个样例里,ZZC学长先用2分钟拿到钥匙5,再用4分钟打开暗门1,最后用5分钟走到出口。

    大体思路:

    多维访问标记数组,每一维对应一种拥有钥匙的状态。

    不同钥匙状态的搜索 在平行空间 同时进行,互不干扰

    这样对一个点的访问动作就有四个属性,x y 为横、纵坐标, s 为钥匙状态, ss为步数。

    人生第一道状压搜索~

    #include<cstdio> #include<queue> #include<cstring> #include<cmath> #include<iostream> using namespace std; #define rep(i,a,b) for(int i=a; i<=b; i++) #define limit 1005 struct node//记录走到一个点时的状态 { int x, y, s, ss;//x y 坐标, s 拥有钥匙的状态, ss 已走过的步数 node(){} node(int xx, int yy, int sta, int stemp) { x = xx, y = yy, s = sta, ss = stemp ;}//构造函数 }; char Map [limit][limit]; bool Vis [20][limit][limit];/********第一位代表当前拥有钥匙的状态*******/ int N, M; int Dx [4] = {0, 0, 1, -1}; int Dy [4] = {1, -1, 0, 0}; queue<node>Q; void bfs () { while(Q.size() ){ node t = Q.front();//从队列中取点 Q.pop(); for(int i=0; i<4; i++){ int x = t.x + Dx[i], y = t.y + Dy[i];//点的移动 int s = t.s, ss = t.ss;//状态与步数 if(Map[x][y] == '#'){//到达终点 printf("%d\n",t.ss+1); return; } if(Map[x][y] == '*') continue;//碰到墙壁 if(Map[x][y] >= '5' && Map[x][y] <= '8'){//发现钥匙 /*******用一个数的二进制低四位表示钥匙有无的状态*******/ int key = Map[x][y] - '5';//获取钥匙编号 key = pow(2,key);//该钥匙的代表状态 s = s|key;//通过 位或 ,把钥匙加入已有钥匙中 } if(Map[x][y] >= '1' && Map[x][y] <= '4'){//发现门 /*******二进制位检查钥匙有无的状态*******/ int key = Map[x][y] - '1';//获取对应钥匙的编号 key = pow(2,key);//该钥匙的代表状态 if( ! (s & key) ) continue;//通过 位与 判断已有钥匙能不能开这扇门 } if( Vis[s][x][y] ) continue;//该点在当前状态下已访问过 //能进行到这里表示这个点可以走 Vis[s][x][y] = 1;//更新访问标记 Q.push(node(x, y, s, ss+1) );//入队 } } printf("-1\n");//始终没能走到终点,输出-1 } int main () { while( ~scanf("%d%d",&N, &M) ){ memset(Vis, 0, sizeof(Vis));//访问标记全部初始化为假 while(Q.size() ) Q.pop();//清空队列 /*******把Map和它的外边界全部用*填满 这样就不用额外考虑坐标越界的情况了*******/ for(int i=0; i<=N+1; i++) for(int j=0; j<=M+1; j++) Map[i][j] = '*'; for(int i=1; i<=N; i++) for(int j=1; j<=M; j++){ scanf(" %c",&Map[i][j]); if(Map[i][j] == '0') {//发现起点 Q.push( node(i, j, 0, 0) );//将该点入队 Vis[0][i][j] = 1;//更新访问标记 } } bfs();//状压宽搜 } return 0; }

    转载请注明原文地址: https://ju.6miu.com/read-8376.html

    最新回复(0)