HDU-OJ-1429 胜利大逃亡(续)

胜利大逃亡(续)

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)

Problem Description
Ignatius再次被魔王抓走了(搞不懂他咋这么讨魔王喜欢)……

这次魔王汲取了上次的教训,把Ignatius关在一个n*m的地牢里,并在地牢的某些地方安装了带锁的门,钥匙藏在地牢另外的某些地方。刚开始Ignatius被关在(sx,sy)的位置,离开地牢的门在(ex,ey)的位置。Ignatius每分钟只能从一个坐标走到相邻四个坐标中的其中一个。魔王每t分钟回地牢视察一次,若发现Ignatius不在原位置便把他拎回去。经过若干次的尝试,Ignatius已画出整个地牢的地图。现在请你帮他计算能否再次成功逃亡。只要在魔王下次视察之前走到出口就算离开地牢,如果魔王回来的时候刚好走到出口或还未到出口都算逃亡失败。
 

Input
每组测试数据的第一行有三个整数n,m,t(2<=n,m<=20,t>0)。接下来的n行m列为地牢的地图,其中包括:

. 代表路
* 代表墙
@ 代表Ignatius的起始位置
^ 代表地牢的出口
A-J 代表带锁的门,对应的钥匙分别为a-j
a-j 代表钥匙,对应的门分别为A-J

每组测试数据之间有一个空行。
 

Output
针对每组测试数据,如果可以成功逃亡,请输出需要多少分钟才能离开,如果不能则输出-1。
 

Sample Input
  
  
4 5 17 @A.B. a*.*. *..*^ c..b* 4 5 16 @A.B. a*.*. *..*^ c..b*
 

Sample Output
  
  
16 -1
 

Author
LL
——————————————————————小小分割线——————————————————————
思路:看起来,比起走迷宫要难的说~但其实并不是那样,这道题并不算难。其实本质上只是多了两个东西:1.钥匙 2.门。 我们先写出一个迷宫的BFS,之后考虑钥匙和门的问题。首先我们知道,在普通的迷宫当中,遇见的只有两种情况,能走 or 不能走。现在我们遇见了,暂时不能走这种情况。vis数组必须做处理。并不是走迷宫那么单纯的访问过就不能再访问。而是,访问了,有可能再访问一次。对了,就是增加状态控制。这个状态不是别的,就是手上拥有的钥匙。还记得连连看那道题吗?
vis[1<<10][x][y] 为什么是1<<10(2的10次方)? 因为依题意最多有10把钥匙。
该数组为了使用位压缩解决该题,增加了[1<<10]这个维度,表示什么?表示钥匙串。
走到每一个点,储存此时手中拥有的钥匙。遇见门的时候,看看手上有没有这道门的钥匙,没有就走,该“钥匙串状态”访问标记赋为1。如果必须需要开这道门,那就只有等到状态更新为拥有此门的钥匙的时候。
代码如下:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#define LIM x >= 0 && x < l && y >= 0 && y < r
struct Node{
    int xx, yy;
    int key;
    int time;
}que[500000];
char s[25];
int mat[25][25], vis[1<<10][25][25];
int dx[4] = {-1,0,1,0}, dy[4] = {0,1,0,-1};
int l, r, sx, sy, tim_li;
int bfs(int x, int y){
    struct Node cur;
    int fron = 0, rear = 0, ki;
    que[rear].xx = x;  que[rear].yy = y;
    que[rear].key = 0;  que[rear++].time = 0;
    vis[0][x][y] = 1;//起点压入栈中
    while(fron < rear){
        cur = que[fron++];
        if(cur.time >= tim_li)
            return -1;//一旦超时,返回-1
        for(int d = 0; d < 4; d++){
            x = cur.xx + dx[d];  y = cur.yy + dy[d];
            ki = cur.key;//更新被访问结点处的位置、钥匙
            if(LIM && mat[x][y] != '*' && !vis[ki][x][y]){//注意!条件是只要不为'*',就可能可以走
                if(mat[x][y] == '.'){
                    vis[ki][x][y] = 1;
                    que[rear].xx = x;  que[rear].yy = y;
                    que[rear].key = ki;
                    que[rear++].time = cur.time+1;
                }//该结点为‘.’,则各种更新
                else if(mat[x][y] == '^'){
                    cur.time++;//出口到了,回城之前记得更新所用时间
                    return (cur.time < tim_li ? cur.time : -1);
                }
                else if(mat[x][y]>='a' && mat[x][y]<='j'){
                    vis[ki][x][y] = 1;
                    ki |= (1<<mat[x][y]-'a');//1<<mat[x][y]-'a'就表示了1~10号钥匙
                    que[rear].xx = x;  que[rear].yy = y;
                    que[rear].key = ki;
                    que[rear++].time = cur.time+1;
                    vis[ki][x][y] = 1;//此处要注意!ki已经变了,此“钥匙串状态”下的访问标记不能不置为1
                }//遇见钥匙,or一下,拿到钥匙。
                else if(mat[x][y]>='A' && mat[x][y]<='J' && (ki & (1<<mat[x][y]-'A'))){
                    vis[ki][x][y] = 1;
                    que[rear].xx = x;  que[rear].yy = y;
                    que[rear].key = ki;
                    que[rear++].time = cur.time+1;
                }//遇见门且能打开,各种更新
            }
        }
    }
    return -1;
}
int main(){
    int i, j;
    while(~scanf("%d%d%d", &l, &r, &tim_li)){
        int k = 0, g = 0;
        for(i = 0; i < l; i++){
            scanf("%s", s);
            for(j = 0; j < r; j++){
                mat[i][j] = s[j];
                if(s[j] == '@'){
                    sx = i;  sy = j;
                    mat[i][j] = '.';//保存起点坐标后,记得设为可以走的点
                }
            }
        }
        memset(vis, 0, sizeof(vis));
        printf("%d\n", bfs(sx, sy));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值