poj 3083 Children of the Candy Corn【BFS+DFS】


poj 3083 Children of the Candy Corn

Children of the Candy Corn
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 9528   Accepted: 4126

Description

The cornfield maze is a popular Halloween treat. Visitors are shown the entrance and must wander through the maze facing zombies, chainsaw-wielding psychopaths, hippies, and other terrors on their quest to find the exit. 

One popular maze-walking strategy guarantees that the visitor will eventually find the exit. Simply choose either the right or left wall, and follow it. Of course, there's no guarantee which strategy (left or right) will be better, and the path taken is seldom the most efficient. (It also doesn't work on mazes with exits that are not on the edge; those types of mazes are not represented in this problem.) 

As the proprieter of a cornfield that is about to be converted into a maze, you'd like to have a computer program that can determine the left and right-hand paths along with the shortest path so that you can figure out which layout has the best chance of confounding visitors.

Input

Input to this problem will begin with a line containing a single integer n indicating the number of mazes. Each maze will consist of one line with a width, w, and height, h (3 <= w, h <= 40), followed by h lines of w characters each that represent the maze layout. Walls are represented by hash marks ('#'), empty space by periods ('.'), the start by an 'S' and the exit by an 'E'. 

Exactly one 'S' and one 'E' will be present in the maze, and they will always be located along one of the maze edges and never in a corner. The maze will be fully enclosed by walls ('#'), with the only openings being the 'S' and 'E'. The 'S' and 'E' will also be separated by at least one wall ('#'). 

You may assume that the maze exit is always reachable from the start point.

Output

For each maze in the input, output on a single line the number of (not necessarily unique) squares that a person would visit (including the 'S' and 'E') for (in order) the left, right, and shortest paths, separated by a single space each. Movement from one square to another is only allowed in the horizontal or vertical direction; movement along the diagonals is not allowed.

Sample Input

2
8 8
########
#......#
#.####.#
#.####.#
#.####.#
#.####.#
#...#..#
#S#E####
9 5
#########
#.#.#.#.#
S.......E
#.#.#.#.#
#########

Sample Output

37 5 5
17 17 9

题意:

三个输出,第一个数据是要求从开始的那个点开始一直沿着左边的强走,一直走到终点,如果没路了就原路返回,返回的路程也算在总步数step里面,第二个则是沿着右边的墙走,第三个是最短路径;


思路:

第三个数据是最好求的,BFS不断扩展就能求出,比较麻烦的是第一个和第二个的数据,因为每到一个地方,可能遍历的方向会不同,也就没办法一个数组就存一个固定遍历方向来求解了,我们需要在dfs的调用参数里面加一个 last_p 来记录上一次的位置状态,这里的状态是一个方向的代表值; 我们需要遍历的方向就只有 4 个,上下左右,对这些方向用数字来标记一下,分别是:

右(0),下(1),左(2),上(3) ,对应的方向数组的下标与状态相同,每走一个地方,用 last_p 来判断该遍历的方向是什么,比如 last_p = 0 ,那么,该遍历的方向是 1,2 , 3 ,0,因为沿着左边的墙走,那么遍历的方向必须是顺时针的,这里的dfs不能标记走过的地方,因为一般情况是需要往回走的,而且返回步数也需要记录下来 ,如果 last_p = 0 ,然后又遍历到 0 这个方向就说明开始往回走了,这是第一个数据的,第二个数据只需要从 结束的点走到开始的点,然后记录步数就行了,因为从开始沿着右边的墙走到终点是逆时针,而从结束点走到开始的点是顺时针,但步数是等价的,这样就不需要写两个dfs了,一个就能算两个的步数,下面给出代码:



#include<iostream>
#include<string>
#include<cstring>
#include<queue>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

char a[45][45];
int x,y,dir[4][2] = {{0,1},{1,0},{0,-1},{-1,0}},book[45][45];  //右下左上  
int ri,le;

struct node {
	int x_,y_,step;
};

int bfs(struct node tp) {
	queue<struct node> qu;
	qu.push(tp);
	book[tp.x_][tp.y_] = 1;
	struct node tmp,Front;
	while(!qu.empty()) {
		Front = qu.front();
		qu.pop();
		if(a[Front.x_][Front.y_] == 'E') return Front.step;
		for(int i = 0 ; i < 4 ; ++i) {
			tmp.x_ = Front.x_ + dir[i][0];
			tmp.y_ = Front.y_ + dir[i][1];
			if(tmp.x_ >= 0 && tmp.x_ < x && tmp.y_ >=0 && tmp.y_ < y && a[tmp.x_][tmp.y_] != '#' && book[tmp.x_][tmp.y_] == 0) {
				tmp.step = Front.step + 1;
				book[tmp.x_][tmp.y_] = 1;
				qu.push(tmp);
			}
		}
	}
	return -1;
}

int flag;

void dfs(int cur_x,int cur_y,int e_x,int e_y,int step,int last_p) {
//	cout << cur_x << " " << cur_y << endl;
	if(cur_x == e_x && cur_y == e_y) {
		le = step;
		flag = 1; 
		return;
	}
	int tmp = (last_p + 1)%4,tp_x,tp_y;
	for(int i = tmp  ;  ; i = (i + 1)%4) { // 方向的遍历,+ 1 % 4,保证状态是在3以内的值 
		tp_x = cur_x + dir[i][0];
		tp_y = cur_y + dir[i][1];
		if(tp_x >= 0 && tp_x < x && tp_y >= 0 && tp_y < y && a[tp_x][tp_y] != '#') {
			dfs(tp_x,tp_y,e_x,e_y,step + 1, (i + 2)%4); // 这里代入的 last_p 要做一些处理,
													// 因为这里算出来的 i 是一个反方向,比如 i 是 2 ,是向下的,那last_p 应该为 4
													// 是 2 的反方向 状态 
			if(flag == 1) return; // 一旦到了终点就不用在dfs了,一路return回起点,时间会快一点 
		}
	}
}


int main(void)
{
	int n,s_x,s_y,e_x,e_y;
	while(scanf("%d",&n)!=EOF) {
		for(int k = 0 ; k < n; ++k ) {
			
				scanf("%d%d",&y,&x);
				for(int i = 0 ; i < x ; ++i) {
					getchar();
					for(int j = 0 ; j < y ; ++j) {
						scanf("%c",&a[i][j]);
						if(a[i][j] == 'S') {
							s_x = i;s_y = j;
						}
						if(a[i][j] == 'E') {
							e_x = i;e_y = j;
						}
					}
				}
				// BFS找最小路径 
				struct node start;
				start.x_ = s_x;start.y_ = s_y;
				start.step = 1;
				memset(book,0,sizeof(book));
				int res;
				res = bfs(start);
				
				int pos; // 判断起点的方向状态 
				
				if(s_x == 0) pos = 3;
				else if(s_x == x - 1) pos = 1;
				else if(s_y == 0) pos = 2;
				else pos = 0;
 				dfs(s_x,s_y,e_x,e_y,1,pos);
 				
 				printf("%d ",le);
 				
 				if(e_x == 0) pos = 3;
				else if(e_x == x - 1) pos = 1;
				else if(e_y == 0) pos = 2;
				else pos = 0;
				dfs(e_x,e_y,s_x,s_y,1,pos);
				
				ri = le;
				printf("%d %d\n",ri,res);
			}
		} 
	return 0;
 }




猜你喜欢

转载自blog.csdn.net/godleaf/article/details/80495211