poj3083(dfs/bfs混合)

链接:http://acm.hust.edu.cn/vjudge/problem/15200

/*
translation:
	先沿着左边的墙从 S 一直走,求到达 E 的步数。
	再沿着右边的墙从 S 一直走,求到达 E 的步数。
	最后求最短路。
	
solution:
	最短路好求。重点是前两步!
	很容易想到前两步用简单的dfs就可以求出。但是关键是要“沿着墙壁走”!
	观察可以发现规律:设当前游戏者朝向为d,则沿着右边墙走时候的检索顺序肯定为d的右边,d,d的左边,d相反的
	方向。沿着左边墙壁走的策略类似,根据这个规律即可写出程序。详见dfs方法中的注释。
	
note:
	1:原来dfs写法是定义一个全局变量ans,然后在每次递归调用时ans++,碰到最终点是返回return语句。但其实
	return为空时会爆栈!!因为只有这一层结束递归,返回上一层后由于上一层没有结束方法的语句,还是会继续递
	归执行,导致爆栈!
	2:这种获得初始朝向的写法是错误的,我觉得逻辑上没问题,不知道错在哪??
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;
const int maxn = 40 + 5;

const int N = 3;
const int S = 1;
const int E = 0;
const int W = 2;

char G[maxn][maxn];
int w, h, ans, vis[maxn][maxn];
int end_row, end_col;

int dir_row[] = {0, 1, 0, -1};
int dir_col[] = {1, 0, -1, 0};

struct Step {
	int r, c, dis;
	Step(int r = 0, int c = 0, int dis = 0) : r(r), c(c), dis(dis) {}
};

inline bool inside(int r, int c) {
	return r >= 0 && r < h && c >= 0 && c < w;
}

int dfs(int dir, int side, int r, int c, int ans) {
	//dir:当前朝向	side:靠左/右墙走		r,c:当前位置		ans:步数
	int nr, nc;
	dir = (dir+4-side)%4;	//找到要开始的那个方向,注意加上4是为了让结果不小于0
	for(int d = 0; d < 4; d++) {
		nr = r + dir_row[dir];
		nc = c + dir_col[dir];

		if(nr == end_row && nc == end_col) return ans+1;	//note1
		if(inside(nr, nc) && G[nr][nc] != '#')
			return dfs(dir, side, nr, nc, ans+1);
		dir = (dir+4+side)%4;	//根据side来求下一个要检索的方向
	}
}

void bfs(int sr, int sc) {
	memset(vis, 0, sizeof(vis));
	queue<Step> q;
	q.push(Step(sr, sc, 1));
	vis[sr][sc] = 1;

	while(!q.empty()) {
		Step u = q.front();	q.pop();

		if(u.r == end_row && u.c == end_col) {
			printf("%d\n", u.dis);
			return;
		}

		for(int d = 0; d < 4; d++) {
			int nr = u.r + dir_row[d];
			int nc = u.c + dir_col[d];
			if(G[nr][nc] != '#' && !vis[nr][nc] && inside(nr, nc)) {
				vis[nr][nc] = 1;
				q.push(Step(nr, nc, u.dis+1));
			}
		}
	}
}

int main()
{
	//freopen("in.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    while(T--) {
		scanf("%d%d", &w, &h);
		for(int i = 0; i < h; i++)	scanf("%s", G[i]);

		int start_row, start_col;
		for(int i = 0; i < h; i++) {
			for(int j = 0; j < w; j++) {
				if(G[i][j] == 'S') {
					start_row = i;	start_col = j;
				}
				if(G[i][j] == 'E') {
					end_row = i;	end_col = j;
				}
			}
		}

		//获得初始的朝向
		int d;
		if(start_row == 0)		d = 1;	//note2
		if(start_row == h-1)	d = 3;
		if(start_col == 0)		d = 0;
		if(start_col == w-1)	d = 2;
		/*
		原来获得初始朝向的写法,错误!!
		for(d = 0; d < 4; d++) {
			int nr = start_row + dir_row[d];
			int nc = start_col + dir_col[d];
			if(G[nr][nc] && inside(nr, nc))	break;
		}
		*/

		//1表示沿着左边墙壁走,-1表示沿着右边墙壁走
		printf("%d ", dfs(d, 1, start_row, start_col, 1));
		printf("%d ", dfs(d, -1, start_row, start_col, 1));

		bfs(start_row, start_col);
    }
    return 0;
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值