题目:给定一个n*m的矩阵,矩阵仅由字母 r , d , e 组成,你可以走上、下、左、右四个方向,每次走一格,但字母迷宫有如下限制:
- 如当前所在格子的字母为 r,那么你将无法前往 d
- 如当前所在格子的字母为 d,那么你将无法前往 e
- 如当前所在格子的字母为 e,那么你将无法前往 r
请问你能否在这种情况下从(1,1)走到(n,m),若能输出最小步数,若不能,输出-1。
样例1:
输入
2 2
r e
d d
输出:2
样例2:
输入
3 4
r d e r
e r d d
d r e r
输出:7
解题思路:图论BFS的题目。主要运用队列和记忆化搜的知识去解决。队列储存着我们目前要扩展的点(已经访问,但是没有访问它的周围点)。而BFS有个特点,那就是第一次访问点(x, y)所需要走的路径必然是最少的,所以记忆化搜可以配合BFS的特点,可以很好求到每个被访问点的最短路径。
class Node {
constructor(data) {
this.data = data;
this.next = null;
}
}
class LinkedList {
constructor() {
this.length = 0;
this.head = null;
}
append(data) {
var newNode = new Node(data);
if (this.length == 0) {
this.head = newNode;
} else {
var current = this.head;
while (current && current.next) {
current = current.next;
}
current.next = newNode;
}
this.length += 1;
}
isEmpty() {
return this.head === null;
}
poll() {
var current = this.head;
this.head = this.head.next;
this.length -= 1;
return current;
}
}
class TwoTuple {
constructor(a, b) {
this.first = a;
this.second = b;
}
}
const main = (n, m, mp) => {
let vis = new Array(n).fill(-1).map(() => new Array(m).fill(-1));
// 表示上下左右四个方向
let fx = [0, 1, 0, -1];
let fy = [1, 0, -1, 0];
const isok = (x, y, last) => {
if (x >= 0 && x < n && y >= 0 && y < m && vis[x][y] === -1) {
// 没有被访问过的点
if ((mp[x][y] !== 'r' && last === 'e') || (mp[x][y] !== 'd' && last === 'r') || (mp[x][y] !== 'e' && last === 'd')) {
return true;
}
}
return false;
};
let q = new LinkedList();
q.append(new TwoTuple(0, 0));
// 第一个路径是0
vis[0][0] = 0;
while (!q.isEmpty()) {
let now = q.poll().data;
for (let i = 0; i < 4; i++) {
let xx = now.first + fx[i];
let yy = now.second + fy[i];
if (isok(xx, yy, mp[now.first][now.second])) {
// 向四周扩散
q.append(new TwoTuple(xx, yy)); // 如果这个点没有被访问过 那就扩展它
vis[xx][yy] = vis[now.first][now.second] + 1;
}
}
}
return vis[n - 1][m - 1];
};
main(2, 2, [
['r', 'e'],
['d', 'd']
]); // 2
main(3, 4, [
['r', 'd', 'e', 'r'],
['e', 'r', 'd', 'd'],
['d', 'r', 'e', 'r']
]); // 7