可重复广搜 —— NYOJ 999 师傅又被妖怪抓走了

对应 NYOJ 题目:点击打开链接

师傅又被妖怪抓走了

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 3
描述

话说唐僧复得了孙行者,师徒们一心同体,共诣西方。自宝象国救了公主,承君臣送出城西,沿路饥餐渴饮,悟空便为师傅去化斋,等悟空回来,悟净慌慌张张的对悟空说:“不好了,不好了”,还没等悟净说完,悟空说:“师傅又被妖怪抓走了”,悟净:“NO ,悟空一脸茫然,悟净:“师傅和二师兄都被妖怪抓走了”。悟空(晕!)。为了防止悟空救人,妖怪先把唐憎和八戒分别藏起来,如果悟空在T分钟之后还没找到人,那必定是被妖怪吃掉了。假设悟空在一个nm列的矩阵内,悟空在每一分钟可以走到上,下,左,右的其中的一个可以走的位置,每次只能走一步。我们把发现定义为可以直接看到对方,也就是说两个人在同一行或者同一列,并且中间没有障碍物或者没有其他人就可以看到对方。

输入
有多组测试数据,每组首先是三个正整数n , m (3<=n,m<=100), T,(0<=T<=100) 分别代表行数,列数,规定的时间。接下来n 行,每行 m 个字符。其中’ S ’ 代表悟空的位置,’ D ’代表师傅位置,’ E ’代表八戒的位置。并且保证都只有一个. ’ X ’代表墙 ,’ . ’代表空地 .
输出
每组先输出一行Case c:(c表示当前的组数,从1开始计数);
接下来一行,如果悟空可以在规定时间内找到两人,则输出最少需要的时间,否则输出-1。
样例输入
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;
}
        






你可能感兴趣的:(可重复广搜 —— NYOJ 999 师傅又被妖怪抓走了)