题目地址:
https://www.lintcode.com/problem/the-minium-distance/description
给定一个 m m m行 n n n列的二维矩阵 A A A,其中 − 2 -2 −2代表起点, − 3 -3 −3代表终点, − 1 -1 −1代表障碍物, 0 0 0代表空地,正整数代表传送门。从起点出发,每次可以朝四个方向走一步,不能走到障碍物上;如果当前位于传送门,则除了能朝四周走之外,还可以花 1 1 1步走到别的编号相同的传送门上。问从起点到终点最少花多少步能到。
思路是双向BFS。直接套用模板即可,只需求下一步能走到的位置的时候考虑一下传送门。代码如下:
import java.util.*;
public class Solution {
class Pair {
int x, y;
public Pair(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
Pair pair = (Pair) o;
return x == pair.x && y == pair.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
/**
* @param grid: a 2D grid
* @return: return the minium distance
*/
public int getMinDistance(int[][] grid) {
// write your code here
// map存传送门,key是编号,value是该编号的传送门坐标
Map<Integer, List<Pair>> map = new HashMap<>();
Pair start = new Pair(0, 0), end = new Pair(0, 0);
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == -2) {
start.x = i;
start.y = j;
} else if (grid[i][j] == -3) {
end.x = i;
end.y = j;
} else if (grid[i][j] > 0) {
map.putIfAbsent(grid[i][j], new ArrayList<>());
map.get(grid[i][j]).add(new Pair(i, j));
}
}
}
Queue<Pair> beginQueue = new LinkedList<>(), endQueue = new LinkedList<>();
Set<Pair> beginSet = new HashSet<>(), endSet = new HashSet<>();
beginQueue.offer(start);
beginSet.add(start);
endQueue.offer(end);
endSet.add(end);
int step = 0;
while (!beginQueue.isEmpty() && !endQueue.isEmpty()) {
step++;
if (oneStep(beginQueue, beginSet, endSet, grid, map)) {
return step;
}
step++;
if (oneStep(endQueue, endSet, beginSet, grid, map)) {
return step;
}
}
return -1;
}
private boolean oneStep(Queue<Pair> beginQueue, Set<Pair> beginSet, Set<Pair> endSet, int[][] grid, Map<Integer, List<Pair>> map) {
int size = beginQueue.size();
for (int i = 0; i < size; i++) {
Pair cur = beginQueue.poll();
for (Pair next : getNexts(cur, grid, beginSet, endSet, map)) {
if (endSet.contains(next)) {
return true;
}
beginQueue.offer(next);
beginSet.add(next);
}
}
return false;
}
// 将能走到的且之前未访问的位置返回
private Set<Pair> getNexts(Pair cur, int[][] grid, Set<Pair> beginSet, Set<Pair> endSet, Map<Integer, List<Pair>> map) {
// 这里用Set的原因是,有可能从某个点走一步能到另一个点,但走传送门也能到那个点,这样就有可能重复计算。所以这里采取Set去一下重
Set<Pair> nexts = new HashSet<>();
int[] d = {1, 0, -1, 0, 1};
int x = cur.x, y = cur.y;
for (int i = 0; i < 4; i++) {
int nextX = x + d[i], nextY = y + d[i + 1];
if (inBound(nextX, nextY, grid) && grid[nextX][nextY] != -1) {
Pair next = new Pair(nextX, nextY);
if (endSet.contains(next)) {
nexts.clear();
nexts.add(next);
return nexts;
}
if (!beginSet.contains(next)) {
nexts.add(next);
}
}
}
// 看一下是否是传送门
if (grid[x][y] > 0) {
for (Pair next : map.get(grid[x][y])) {
if (endSet.contains(next)) {
nexts.clear();
nexts.add(next);
return nexts;
}
if (!beginSet.contains(next)) {
nexts.add(next);
}
}
}
return nexts;
}
private boolean inBound(int x, int y, int[][] grid) {
return 0 <= x && x < grid.length && 0 <= y && y < grid[0].length;
}
}
时空复杂度 O ( m n ) O(mn) O(mn)。