hdu 1429胜利大逃亡(续) (bfs+状态压缩)
胜利大逃亡(续)
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6652 Accepted Submission(s): 2314
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
/************************************************************************* > File Name: code/hdu/1429.cpp > Author: 111qqz > Email: rkz2013@126.com > Created Time: 2015年08月12日 星期三 15时56分05秒 ************************************************************************/ #include<iostream> #include<iomanip> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<string> #include<map> #include<set> #include<queue> #include<vector> #include<stack> #define y0 abc111qqz #define y1 hust111qqz #define yn hez111qqz #define j1 cute111qqz #define tm crazy111qqz #define lr dying111qqz using namespace std; #define REP(i, n) for (int i=0;i<int(n);++i) typedef long long LL; typedef unsigned long long ULL; const int inf = 0x7fffffff; const int N=21; int n,m,t; int dx[4]={-1,1,0,0}; int dy[4]={0,0,-1,1}; int vis[N][N][1025]; int ans; char maze[N][N]; struct node { int x,y; int key; int time; bool ok() { if (x>=0&&x<n&&y>=0&&y<m&&maze[x][y]!='*') return true; return false; } }s; void bfs() { queue<node>Q; Q.push(s); while (!Q.empty()) { node pre; pre = Q.front();Q.pop(); if (maze[pre.x][pre.y]=='^') { ans = pre.time; return; } for ( int i = 0 ; i < 4 ; i++) { node next; next.x = pre.x + dx[i]; next.y = pre.y + dy[i]; next.time = pre.time + 1; next.key = pre.key; if (!next.ok()) continue; if (maze[next.x][next.y]>='a'&&maze[next.x][next.y]<='j') { int nkey = 1<<(maze[next.x][next.y]-'a'); //计算当前钥匙在钥匙串中的位置 next.key = (next.key | nkey); //将当前钥匙添加到钥匙串(状态压缩重点!) if (!vis[next.x][next.y][next.key]) { vis[next.x][next.y][next.key]=true; Q.push(next); } } else if (maze[next.x][next.y]>='A'&&maze[next.x][next.y]<='J') { int needkey = (maze[next.x][next.y]-'A');// 计算当前门所需要的钥匙在钥匙串中的位置 if (((next.key>>needkey)&1)&&!vis[next.x][next.y][next.key]) //next.key>>needkey 取出所需钥匙的状态 (状态压缩重点!) { vis[next.x][next.y][next.key] = true; Q.push(next); } } else { if (!vis[next.x][next.y][next.key]) { vis[next.x][next.y][next.key] = true; Q.push(next); } } } } } int main() { while (scanf("%d %d %d",&n,&m,&t)!=EOF) { memset(vis,false,sizeof(vis)); for ( int i = 0 ; i < n ; i++ ) { scanf("%s",maze[i]); } string kong; // cin>>kong; for ( int i = 0 ; i < n ; i++ ) { for ( int j = 0 ; j < m ; j ++ ) { if (maze[i][j]=='@') { s.x = i; s.y = j; } } } s.key = 0; s.time =0; vis[s.x][s.y][s.key]=true; ans = inf/2; //走不出可能是时间不够,也可能是时间够也走不出去(本身迷宫没有通路),因为忽略后一种情况wa了几次== bfs(); if (ans<=t-1) { cout<<ans<<endl; } else { cout<<-1<<endl; } } return 0; }
Source
这道题不错.
之前做过一个按照顺序找食物的,这道题和那个不一样.
并不能用一般的dfs做掉,因为可能先遇到的门的钥匙在后拓展的路径才能遇到,这样标记了的话就回不来了.
由于钥匙最多才10把,我们考虑状态压缩,一个十位二进制数表示当前拥有的"钥匙串"
当遇到一个小写字母(钥匙)的时候,把这把钥匙添加到钥匙串中.比如我现在的钥匙串是0000001010,表示我有b,d两把钥匙这个时候我遇到了f
那么我们先计算当前钥匙在钥匙串中所在的位置,构建一个只含这把钥匙的钥匙串 1<<('f'-'a') 为 0000100000,
然后通过|(逻辑或)运算,把这个钥匙串添加到之前的钥匙串中 0000001010|0000100000 = 0000101010 表示我现在有了 b,d,f三把钥匙.
对于遇到门的时候,我们要看这把门的钥匙在不在.做法是把这把门的钥匙所在的位置的状态取出来.
比如我遇到了门D,当前的钥匙串是0000101010 , 0000101010>>(D-'A')=0000101 ,此时最后一位就是D门的状态
0000101&1判断最后一位是否为1.
其他就是一个朴素的bfs
喵呜,第一次用结构体写.
还有一点需要注意的是,无法逃脱可能有两种情况,一种是因为时间不够逃脱,另一种是迷宫本身没有通路导致不能逃脱,因为这个wa了好几次
把 ans 初始设置成inf 就好.
具体见代码