题目
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
题解
总觉得上面背景里的意思和样例不一样……
这种错觉导致的问题就是,理解错了题意
最初理解的是:在t时间后传送回初始位置,但是不会死,而是继续寻路(可以在t时间内拿到钥匙,开t时间距离的门,即使钥匙距离大于t/2)
在这个错误的思路下,写出了一个错误的答案,不知道能不能“AC”
不过也算一种相似题型的思路吧
/* HDU 1429.胜利大逃亡(续) 的错误理解 理解为:t时间后强行传送到起点(不结束,还能继续走) */ /* By:OhYee Github:OhYee Email:oyohyee@oyohyee.com Blog:http://www.cnblogs.com/ohyee/ かしこいかわいい? エリーチカ! 要写出来Хорошо的代码哦~ */ #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <string> #include <iostream> #include <vector> #include <list> #include <queue> #include <stack> #include <map> using namespace std; //DEBUG MODE #define debug 0 //循环 #define REP(n) for(int o=0;o<n;o++) const int maxn = 25; char Map[maxn][maxn]; int dis[maxn][maxn]; int n,m,t; typedef pair<int,int> point; const int delta[] = {1,-1,0,0}; int BFS(int s1,int s2,int v1,int v2) { queue<point> Q,door; memset(dis,-1,sizeof(dis)); bool key[10]; memset(key,false,sizeof(key)); Q.push(point(s1,s2)); dis[s1][s2] = 0; while(!Q.empty() || !door.empty()) { if(!Q.empty()) { int x = Q.front().first; int y = Q.front().second; Q.pop(); REP(4) { int xx = x + delta[o]; int yy = y + delta[3 - o]; //非法访问 if(xx < 0 || xx >= n || yy < 0 || yy >= m) continue; //墙 if(Map[xx][yy] == '*') continue; //钥匙 if(Map[xx][yy] >= 'a' && Map[xx][yy] <= 'j') key[Map[xx][yy] - 'a'] = true; //门 if(Map[xx][yy] >= 'A' && Map[xx][yy] <= 'J') if(!key[Map[xx][yy] - 'A']) door.push(point(xx,yy)); //更新节点 int temp = dis[xx][yy]; dis[xx][yy] = (dis[xx][yy] == -1 ? dis[x][y] + 1 : min(dis[x][y] + 1,dis[xx][yy])); //剪枝:如果已超过时间,就不再考虑 if(dis[xx][yy] >= t) continue; if(temp != dis[xx][yy]) Q.push(point(xx,yy)); } } else { int size = door.size(); bool flag = false; while(size--) { int x = door.front().first; int y = door.front().second; door.pop(); if(key[Map[x][y] - 'A']) { Q.push(point(x,y)); flag = true; break; } door.push(point(x,y)); } if(flag) continue; else break; } } return dis[v1][v2]; } bool Do() { if(scanf("%d%d%d",&n,&m,&t) == EOF) return false; int s1,s2,v1,v2; for(int i = 0;i < n;i++) for(int j = 0;j < m;j++) { scanf("\n%c\n",&Map[i][j]); if(Map[i][j] == '@') { s1 = i; s2 = j; } if(Map[i][j] == '^') { v1 = i; v2 = j; } } printf("%d\n",BFS(s1,s2,v1,v2)); return true; } int main() { while(Do()); return 0; }
换回正确思路,我们发现,需要保存有哪些钥匙
对于a-j 10把钥匙,我们共有1024种可能
因此,我们可以采用二进制来记录钥匙的集合
//返回新的钥匙集合 //参数:原始的钥匙集合 获得的钥匙的编号 inline int get_key(int key,int num) { return key | (1 << num); } //返回是否存在门的钥匙 //参数:钥匙集合 门的编号 inline bool has_key(int key,int num) { return (key & (1 << num)) > 0; }
然后看成3维空间的BFS,层数代表钥匙的集合
由于可能有1024层,因此要即时剪枝
(觉得会有更好的算法来解决吧,整个三维数组开出来有640000,略大)
另外,晚上真的不适合做题……
删错行,把if当成while找bug……
代码
/* By:OhYee Github:OhYee Email:oyohyee@oyohyee.com Blog:http://www.cnblogs.com/ohyee/ かしこいかわいい? エリーチカ! 要写出来Хорошо的代码哦~ */ #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <string> #include <iostream> #include <vector> #include <list> #include <queue> #include <stack> #include <map> using namespace std; //DEBUG MODE #define debug 0 //循环 #define REP(n) for(int o=0;o<n;o++) const int maxn = 25; char Map[maxn][maxn]; int dis[maxn][maxn][1024]; int n,m,t; const int delta[] = {1,-1,0,0}; struct point { int x,y; int key; point(int a,int b,int c) { x = a; y = b; key = c; } }; //返回新的钥匙集合 //参数:原始的钥匙集合 获得的钥匙的编号 inline int get_key(int key,int num) { return key | (1 << num); } //返回是否存在门的钥匙 //参数:钥匙集合 门的编号 inline bool has_key(int key,int num) { return (key & (1 << num)) > 0; } int BFS(int s1,int s2,int v1,int v2) { int Min = -1; queue<point> Q; memset(dis,-1,sizeof(dis)); Q.push(point(s1,s2,0)); dis[s1][s2][0] = 0; while(!Q.empty()) { int x = Q.front().x; int y = Q.front().y; int key = Q.front().key; Q.pop(); REP(4) { int xx = x + delta[o]; int yy = y + delta[3 - o]; int kk = key; //非法访问 if(xx < 0 || xx >= n || yy < 0 || yy >= m) continue; //墙 if(Map[xx][yy] == '*') continue; //钥匙 if(Map[xx][yy] >= 'a' && Map[xx][yy] <= 'j') kk = get_key(kk,Map[xx][yy] - 'a'); //门 if(Map[xx][yy] >= 'A' && Map[xx][yy] <= 'J') if(!has_key(kk,Map[xx][yy] - 'A')) continue; //更新节点 if(dis[xx][yy][kk] == -1) { dis[xx][yy][kk] = dis[x][y][key] + 1; //剪枝:如果已超过时间,就不再考虑 if(dis[xx][yy][kk] >= t) continue; if(xx == v1 && yy == v2) { Min = ((Min == -1) ? dis[xx][yy][kk] : min(Min,dis[xx][yy][kk])); continue; } Q.push(point(xx,yy,kk)); } } } return Min; } bool Do() { if(scanf("%d%d%d",&n,&m,&t) == EOF) return false; int s1,s2,v1,v2; for(int i = 0;i < n;i++) for(int j = 0;j < m;j++) { scanf("\n%c",&Map[i][j]); if(Map[i][j] == '@') { s1 = i; s2 = j; } if(Map[i][j] == '^') { v1 = i; v2 = j; } } printf("%d\n",BFS(s1,s2,v1,v2)); return true; } int main() { while(Do()); return 0; }