对应 NYOJ 题目:点击打开链接
话说唐僧复得了孙行者,师徒们一心同体,共诣西方。自宝象国救了公主,承君臣送出城西,沿路饥餐渴饮,悟空便为师傅去化斋,等悟空回来,悟净慌慌张张的对悟空说:“不好了,不好了”,还没等悟净说完,悟空说:“师傅又被妖怪抓走了”,悟净:“NO!” ,悟空一脸茫然,悟净:“师傅和二师兄都被妖怪抓走了”。悟空(晕!)。为了防止悟空救人,妖怪先把唐憎和八戒分别藏起来,如果悟空在T分钟之后还没找到人,那必定是被妖怪吃掉了。假设悟空在一个n行m列的矩阵内,悟空在每一分钟可以走到上,下,左,右的其中的一个可以走的位置,每次只能走一步。我们把发现定义为可以直接看到对方,也就是说两个人在同一行或者同一列,并且中间没有障碍物或者没有其他人就可以看到对方。
5 6 3 XXD... ....E. ....X. ....S. ...... 5 6 3 XDX... ....E. ...... ....S. ...... 5 6 8 XXDX.. .XEX.. ...... ....S. ......
Case 1: -1 Case 2: 3 Case 3: -1
题意:
中文题
思路:
首先拷贝一份地图,把 D 和 E 所在的行和列直接可达的空点分别用 D 和 E 填充,像这样:
其中,D 和 E 的交点要用另一个识别符(如 ‘H’ ),表示该点可以同时找到 D 和 E
然后从起点 S 进行 BFS,队列里的元素为:
typedef struct{ int r, c; /* 横纵坐标 */ int countD, countE; /* 在该点是否能找到 D,E */ int step; /* 从起点到该点的步数 */ }Point;
BFS 是扩散式搜索,所以第一个符合题意的点(即 countD 和 countE 都不为 0,或者找到了D 和 E 的交点)就可以返回该点的 step。存在的问题是,该题很明显是可重复搜索,而一般 BFS 是不可重复的,如果像普通 BFS 那样只加个 vis[][] 标记已搜索的点,将得到错误的结果;如果不加 vis[][] 标记,答案是正确的,不过也很明显会超时。那怎么办?可以在 vis[][] 的前提下再加两个标记数组 visD[][] 和 visE[][],分别表示该点能否找到 D 或 E。接下来对于一个已经搜索过的点的坐标为 (x, y),它的父节点为 fa,如果 visD[x][y] != fa->countD || visE[x][y] != fa->countE(即该点与父节点的状态不一致),则这个点是可重复搜索的,否则是不可搜索。画图就能理解。
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXN 110 #define QUE_SIZE 100000 char _map0[MAXN][MAXN]; char _map[MAXN][MAXN]; char vis[MAXN][MAXN]; char visD[MAXN][MAXN]; char visE[MAXN][MAXN]; int gr[4] = {-1, 0, 1, 0}; int gc[4] = {0, 1, 0, -1}; int n, m, t; typedef struct{ int r, c; /* 横纵坐标 */ int countD, countE; /* 在该点是否能找到 D,E */ int step; /* 从起点到该点的步数 */ }Point; Point que[QUE_SIZE]; /* 把 D 和 E 所在的行和列直接可达的空点分别用 D 和 E 填充 */ void change_point(char ch, int r, int c) { int i; /* up */ for(i = r-1; i >= 0; i--) if(_map[i][c] == '.' || _map[i][c] == 'S') _map[i][c] = ch; else if(_map0[i][c] == 'D' || _map0[i][c] == 'E' || _map0[i][c] == 'X') break; else if(_map[i][c] == 'D' || _map[i][c] == 'E') _map[i][c] = 'H'; /* down */ for(i = r+1; i < n; i++) if(_map[i][c] == '.' || _map[i][c] == 'S') _map[i][c] = ch; else if(_map0[i][c] == 'D' || _map0[i][c] == 'E' || _map0[i][c] == 'X') break; else if(_map[i][c] == 'D' || _map[i][c] == 'E') _map[i][c] = 'H'; else break; /* left */ for(i = c-1; i >= 0; i--) if(_map[r][i] == '.' || _map[r][i] == 'S') _map[r][i] = ch; else if(_map0[r][i] == 'D' || _map0[r][i] == 'E' || _map0[r][i] == 'X') break; else if(_map[r][i] == 'D' || _map[r][i] == 'E') _map[r][i] = 'H'; else break; /* right */ for(i = c+1; i < m; i++) if(_map[r][i] == '.' || _map[r][i] == 'S') _map[r][i] = ch; else if(_map0[r][i] == 'D' || _map0[r][i] == 'E' || _map0[r][i] == 'X') break; else if(_map[r][i] == 'D' || _map[r][i] == 'E') _map[r][i] = 'H'; else break; } /* 计算该点能找到谁 */ void get_count(Point *p) { int r = p->r; int c = p->c; if(_map[r][c] == 'D'){ p->countD = 1; } else if(_map[r][c] == 'E'){ p->countE = 1; } else if(_map[r][c] == 'H'){ p->countD = p->countE = 1; } } int go_find(int sr, int sc) { int i, j; int front, rear; Point sun; for(i = 0; i < n; i++){ for(j = 0; j < m; j++){ char ch = _map0[i][j]; if(ch == 'D' || ch == 'E') change_point(ch, i, j); } } #if 0 for(i = 0; i < n; i++) printf("%s\n", _map[i]); #endif memset(vis, 0, sizeof(vis)); memset(visD, 0, sizeof(visD)); memset(visE, 0, sizeof(visE)); front = 0; rear = 1; sun.r = sr; sun.c = sc; sun.countD = 0; sun.countE = 0; sun.step = 0; get_count(&sun); vis[sr][sc] = 1; visD[sr][sc] = sun.countD; visE[sr][sc] = sun.countE; que[front] = sun; _map0[sr][sc] = '.'; /* 起点也是可走的 */ while(front < rear){ Point cp, cpt; cp = cpt = que[front++]; if(cp.step > t) /* 超过限制步数 */ return -1; if(cp.countD && cp.countE){ return cp.step; } for(i = 0; i < 4; i++){ int r = cp.r + gr[i]; int c = cp.c + gc[i]; if(r >= 0 && r < n && c >= 0 && c < m && _map0[r][c] == '.'){ if(!vis[r][c] || (visD[r][c] != cp.countD || visE[r][c] != cp.countE)){ cp.r = r; cp.c = c; get_count(&cp); vis[r][c] = 1; visD[r][c] = cp.countD; visE[r][c] = cp.countE; cp.step++; que[rear++] = cp; cp = cpt; } } } } return -1; } int main() { #if 0 freopen("in.txt","r",stdin); #endif int w = 0; while(~scanf("%d%d%d", &n, &m, &t)){ int i, j, ans; int sr, sc; for(i = 0; i < n; i++){ scanf("%s", _map[i]); for(j = 0; j < m; j++){ if(_map[i][j] == 'S'){ sr = i; sc = j; } } } for(i = 0; i < n; i++) strcpy(_map0[i], _map[i]); ans = go_find(sr, sc); printf("Case %d:\n", ++w); printf("%d\n", ans>t?-1:ans); } return 0; }