胜利大逃亡(续)
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已画出整个地牢的地图。现在请你帮他计算能否再次成功逃亡。只要在魔王下次视察之前走到出口就算离开地牢,如果魔王回来的时候刚好走到出口或还未到出口都算逃亡失败。
这次魔王汲取了上次的教训,把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
每组测试数据之间有一个空行。
. 代表路
* 代表墙
@ 代表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。如果必须需要开这道门,那就只有等到状态更新为拥有此门的钥匙的时候。
代码如下:
——————————————————————小小分割线——————————————————————
思路:看起来,比起走迷宫要难的说~但其实并不是那样,这道题并不算难。其实本质上只是多了两个东西: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;
}