1、常见迷宫问题
回溯、dfs:是否有解,唯一解等问题
//递归写法
public static boolean dfs(char[][] m, boolean[][] vis, Node start, Node end) {
if (start.x == end.x && start.y == end.y) {
return true;
}
vis[start.x][start.y] = true;
for (int i = 0; i < 4; i++) {
int x = start.x + xx[i];
int y = start.y + yy[i];
if (check(m, vis, x, y)) {
if (dfs(m, vis, new Node(x, y), end) ) //有一个trur就行了
return true;
}
}
return false;
}
//栈模拟写法
public static boolean dfs2(char[][] m, boolean[][] vis, Node start, Node end) {
vis[start.x][start.y] = true;
Deque<Node> st = new LinkedList<>(); //模拟栈
st.push(start);
while (!st.isEmpty()) {
Node tmp = st.pop();
if (tmp.x == end.x && tmp.y == end.y) {
return true;
}
vis[tmp.x][tmp.y] = true;
//tmp.print();
for (int i = 0; i < 4; i++) {
int x = tmp.x + xx[i];
int y = tmp.y + yy[i];
if (check(m, vis, x, y)) {
st.push(new Node(x, y));
}
}
}
return false;
}
BFS:走出迷宫的最短路径:
class Node {
int x, y;
//int len; //有时需要记录深度
//Node pre; //有事需要记录路劲
}
public static int BFS(char[][] m, boolean[][] vis, Node start, Node end,) {
Queue<Node> q = new LinkedList<>();
q.offer(new Node(start.x, start.y, 0)); // 起点加入队列
while (!q.isEmpty()) {
Node tmp = q.poll();
vis[tmp.x][tmp.y] = true;
if (tmp.x == end.x && tmp.y == end.y) { //到大终点
return tmp.len;
}
for (int i = 0; i < 4; i++) { // 上下左右四个方向 并检查坐标的合法性及是否访问
int x = tmp.x + xx[i];
int y = tmp.y + yy[i];
if (check(m, vis, x, y, c)) {
Node newTmp = new Node(x, y, tmp.len + 1);
q.offer(newTmp);
}
}
}
return -1;
}
2、一道难题
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
这是一个关于二维迷宫的题目。我们要从迷宫的起点 'S' 走到终点 'E',每一步我们只能选择上下左右四个方向中的一个前进一格。 'W' 代表墙壁,是不能进入的位置,除了墙壁以外的地方都可以走。迷宫内的 'D' 代表一道上锁的门,只有在持有钥匙的时候才能进入。而 'K' 则代表了钥匙,只要进入这一格,就会自动地拿到钥匙。最后 '.' 则是代表空无一物的地方,欢迎自在的游荡。
本题的迷宫中,起点、终点、门跟钥匙这四个特殊物件,每一个恰好会出现一次。而且,此迷宫的四周 (最上面的一行、最下面的一行、最左边的一列以及最右边的一列) 都会是墙壁。
请问,从起点到终点,最少要走几步呢?
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
class Node {
int x, y, len;
public Node(int a, int b, int l) {
x = a;
y = b;
len = l;
}
public Node() {
}
public void print() {
System.out.println(x + " " + y + " " + len);
}
}
public class Main {
static int[] xx = new int[]{1, 0, -1, 0};
static int[] yy = new int[]{0, 1, 0, -1};
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int m = in.nextInt();
int n = in.nextInt();
char[][] a = new char[m][n];
boolean[][] vis = new boolean[m][n];
in.nextLine();
Node start = new Node(), end = new Node(-1, -1, 0), key = new Node(-1, -1, 0), men = new Node(-1, -1, 0);
for (int i = 0; i < m; i++) {
String s = in.nextLine();
for (int j = 0; j < n; j++) {
a[i][j] = s.charAt(j);
if (s.indexOf('S') != -1) {
start.x = i;
start.y = s.indexOf('S');
}
if (s.indexOf('K') != -1) {
key.x = i;
key.y = s.indexOf('K');
}
if (s.indexOf('E') != -1) {
end.x = i;
end.y = s.indexOf('E');
}
if (s.indexOf('D') != -1) {
men.x = i;
men.y = s.indexOf('D');
}
}
}
int zeroStep = BFS(a, vis, start, end, 'D');
//System.out.println(zeroStep);
//System.out.println();
fillFalse(vis);
int firstStep = BFS(a, vis, start, key, 'D');
fillFalse(vis);
//System.out.println(firstStep);
int secondStep = BFS(a, vis, key, men, ' ');
fillFalse(vis);
//System.out.println(secondStep);
int thireStep = BFS(a, vis, men, end, ' ');
//System.out.println(zeroStep + " " + firstStep + " " + secondStep + " " + thireStep);
int res = firstStep + secondStep + thireStep;
if (zeroStep != -1 && zeroStep < res) {
System.out.println(zeroStep);
return;
}
if ((firstStep == -1 || secondStep == -1 || thireStep == -1) && zeroStep == -1) {
System.out.println(-1);
return;
} else
System.out.println(firstStep + secondStep + thireStep);
//System.out.println("res:"+firstStep +" " +secondStep+" "+thireStep);
}
public static int BFS(char[][] m, boolean[][] vis, Node start, Node end, char c) {
Queue<Node> q = new LinkedList<>();
q.offer(new Node(start.x, start.y, 0));
while (!q.isEmpty()) {
Node tmp = q.poll();
//tmp.print();
vis[tmp.x][tmp.y] = true;
if (tmp.x == end.x && tmp.y == end.y) {
return tmp.len;
}
for (int i = 0; i < 4; i++) {
int x = tmp.x + xx[i];
int y = tmp.y + yy[i];
if (check(m, vis, x, y, c)) {
Node newTmp = new Node(x, y, tmp.len + 1);
q.offer(newTmp);
}
}
}
return -1;
}
public static boolean check(char[][] m, boolean[][] vis, int x, int y, char c) {
if (x < 0 || y < 0 || x > m.length - 1 || y > m[0].length - 1 ||
m[x][y] == c || vis[x][y] || m[x][y] == 'W') { //w表示障碍
return false;
}
return true;
}
public static void fillFalse(boolean[][] vis) {
for (int i = 0; i < vis.length; i++) {
for (int j = 0; j < vis[0].length; j++) {
vis[i][j] = false;
}
}
}
}
总结:
1、无障碍,右下走,代价相等 (dp[i][j] = dp[i][j-1] + dp[i-1][j] 或者组合公式)
变式:有障碍,右下走,代价相等 dp即可
变式:无障碍,右下走,代价不等 dp即可dp[i][j] = min(dp[i][j-1] + dp[i-1][j]) + a[i][j]
变式:无障碍,右下走,代价不等并可能为负值
题目示例:leetcode 174. 地下城游戏 (反向推)
2、有障碍,任意走,代价相等(大于等于0)(一般性迷宫)
解法很经典:DFS或BFS