算法笔记练习 8.2 广度优先搜索(BFS) 问题 B: DFS or BFS?

算法笔记练习 题解合集

本题链接

题目

题目描述
说好了,题目不黑人。

给你一个8*8的矩阵,你的初始位置是左下角方格(用’U’表示),你的目标位置是右上角的方格(用’A’表示),其余的62个方格,如果是’.’,表示这个方格为空,如果是’S’,表示这个方格有一块大石头。好了现在你开始从左下角出发,每次可以往上,下,左,右,左上,右上,左下,右下移动一个方格,或者你可以原地不动,一共九个动作方式,在你做完一个动作后,所有的大石头会往下掉一个方格(如果一个大石头的位置是(x,y),那下一秒是(x+1,y),不过如果它已经在最下面的一排了,那它就会掉出矩阵,不再出现),请注意,任一时刻,你不能和某一个大石头处在同一个方格,否则石头会把你XX掉。

现在的问题就是:你能从左下角安全抵达右上角么? 如果能,输出“Yes”,反之,“No”。

输入
T->测试数据组数(T)。

对于每组数据,输入一个8*8的矩阵,其后有一空行。描述如上。

输出
对于第i组数据,请输出

Case #i: s(s是一个字符串,如果可以到达,则s为“Yes”,反之“No”)

样例输入

2
.......A
........
........
........
........
........
........
U.......

.......A
........
........
........
........
.S......
S.......
US......

样例输出

Case #1: Yes
Case #2: No

思路

建议先把这一节练习的后面三个题做掉再回来写这个题,关于 BFS 的大思路总结请参考 问题 C: 【宽搜入门】8数码难题,以及 问题 D: 【宽搜入门】魔板


如果每次移动后让数组里代表石头的元素真的都下移一个单位,时间复杂度和空间复杂度都会变得不能接受。所以在模拟的时候把石头下降改成地图视野整体向上移动一格(想象成竖向的卷轴类动作游戏),与此同时人所在的位置也跟着地图向上移动,原先的规则改成下面这样:

  1. 令变量bottom表示目前地图行数的下界,初始值为起点所在的行。由于地图视野随着移动次数的增加而向上移,在静态的数组中的体现就是每移动一次,bottom都要减 1,且每次移动时,位于bottom下面的行是不能去的;
  2. 需要三个变量保存当前的状态:人物目前在数组中的两个坐标,以及变量bottom
  3. 理论上每个状态都有 9 种可能的变化,如何判断变化是否合法?
    a. 先不管石头砸下来的事情,首先要去的地方本身不能越界,即不能越过数组的边界,不能去bottom下面的行;
    b. 移动之后因为人物会强制上移一格,所以检查人物上方的格子是不是石头,如果是石头就被砸死了;
    c. 如果以上两点都没有问题,判断人物自行移动以及强制上移一格后,这时的坐标和bottom的值是否曾经出现过(避免重复,用<set>即可实现),若没出现过则是合法的,将其入队。
  4. 如果bottom移动到终点的上面一行或者人物移动到终点的上面一行,说明成功了。(为什么?因为这样可以保证所有石头都不会再出现了,再也不会被石头砸死了,可以随心所欲地走到终点去,自然就成功了。所以这里还能做一个剪枝就是判断:包括人物所在行以上的地方有没有石头,如果没有就直接成功。不过这个题数据小,没有剪的必要)

因为上述的第 4 点,即关于成功的判断需要人物走到终点上面一行甚至两行(例如位于终点那一行的时候人物选择向上移动,移动后又被强制向上移动),所以存储迷宫的数组maze的大小设置成 10 × 8,前两行都没有石头,后面八行用来接收输入。

代码

#include <iostream>
#include <vector>
#include <queue>
#include <set>
using namespace std;

bool safe;
vector<vector<bool> > maze(10, vector<bool>(8, true));
set<vector<int> > states;

void getMaze() {
	string input;
	getline(cin, input);
	for (int i = 2; i <= 9; ++i) {
		getline(cin, input);
		for (int j = 0; j < 8; ++j)
			maze[i][j] = (input[j] == 'S' ? false : true);
	} 
}
void BFS() {
	states.clear();
	states.insert( {9, 0, 9} );
	queue<vector<int> > Q;
	Q.push( {9, 0, 9} );
	while (!Q.empty()) {
		vector<int> top = Q.front();
		if (top[2] < 2 || top[0] < 2) {
			safe = true;
			return;
		}
		Q.pop();
		for (int i = -1; i <= 1; ++i) {
			for (int j = -1; j <= 1; ++j) {
				if (top[0] + i <= top[2] && top[0] + i >= 0
				 && top[1] + j >= 0 && top[1] + j <= 8
				 && maze[top[0] + i][top[1] + j] == true
				 && maze[top[0] + i - 1][top[1] + j] == true
				 && !states.count( {top[0] + i - 1, top[1] + j, top[2] - 1} ))
					Q.push( {top[0] + i - 1, top[1] + j, top[2] - 1} );
					states.insert( {top[0] + i - 1, top[1] + j, top[2] - 1} );
			} 
		} 
	} 
} 

int main() {
	int T;
	while (scanf("%d", &T) != EOF) {
		for (int i = 1; i <= T; ++i) {
			safe = false;
			getMaze();
			BFS();
			printf("Case #%d: %s\n", i, safe ? "Yes" : "No"); 
		}
	} 
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值