Leetcode 1976. Number of Ways to Arrive at Destination
Woodyiiiiiii opened this issue · comments
这道题我一开始直接用BFS做,没用PQ,然后报错了。看了解析才知道,要结合PQ,说明我对BFS+PQ的理解还是不够深入,不知道什么时候用PQ以及为什么要用PQ。
总结如下:这种在图中要求找到一个顶点到另一个顶点的最短路径之类的题型,都可以用BFS+PQ来做,其中BFS+PQ结合起来就是Dijkstra算法!
可以总结以下题目找到规律:
- 1976. Number of Ways to Arrive at Destination
- 2290. Minimum Obstacle Removal to Reach Corner
- 1293. Shortest Path in a Grid with Obstacles Elimination
- 2577. Minimum Time to Visit a Cell In a Grid
- 778. Swim in Rising Water
- 1514. Path with Maximum Probability (无向图)
为什么要用PQ而不是普通的队列Queue呢,以此题为例,case是
6
[[3,0,4],[0,2,3],[1,2,2],[4,1,3],[2,5,5],[2,3,1],[0,4,1],[2,4,6],[4,3,1]]
画完图,然后在Queue代码的情况下跑一下:
public int countPaths(int n, int[][] roads) {
// convert roads to graph
Map<Long, List<long[]>> graph = new HashMap<>();
for (int[] road : roads) {
long from = road[0];
long to = road[1];
long cost = road[2];
graph.putIfAbsent(from, new ArrayList<>());
graph.get(from).add(new long[]{to, cost});
graph.putIfAbsent(to, new ArrayList<>());
graph.get(to).add(new long[]{from, cost});
}
// bfs
Queue<long[]> queue = new LinkedList<>();
// PriorityQueue<long[]> queue = new PriorityQueue<>((o1, o2) -> o2[1] - o1[1] >= 0 ? -1 : 1);
long[][] dist = new long[n][2];
for (int i = 0; i < n; i++) {
dist[i][0] = Long.MAX_VALUE;
dist[i][1] = 0;
}
dist[0][0] = 0;
dist[0][1] = 1;
final int mod = (int)1e9 + 7;
queue.offer(new long[]{0, 0});
while (!queue.isEmpty()) {
long[] cur = queue.poll();
if (!graph.containsKey(cur[0])) continue;
for (long[] next : graph.get(cur[0])) {
int nextNode = (int) next[0];
int nextCost = (int) next[1];
if (nextCost + cur[1] < dist[nextNode][0]) {
dist[nextNode][1] = dist[(int) cur[0]][1];
dist[nextNode][0] = nextCost + cur[1];
queue.offer(new long[]{nextNode, dist[nextNode][0]});
} else if (nextCost + cur[1] == dist[nextNode][0]) {
dist[nextNode][1] += dist[(int) cur[0]][1];
dist[nextNode][1] %= mod;
// queue.offer(new long[]{nextNode, dist[nextNode][0]});
}
}
}
return (int) dist[n - 1][1];
}
就会发现,最后答案会变少,因为路径被截断了,在顶点3的情况下,到2后就不再往5前进了,因为之前已经有路径经过,所以停止了,最后结果为1;
而使用PQ后,因为PQ的排序,可以将队列内的路径“齐头并进”,这样上述在顶点3的路径就始终汇总成1条,继续前进。
宏观来说,BFS+PQ的结合,实际上就是用Dijkstra算法求最短路径,然后在运算过程中记录经过次数。
public int countPaths(int n, int[][] roads) {
// convert roads to graph
Map<Long, List<long[]>> graph = new HashMap<>();
for (int[] road : roads) {
long from = road[0];
long to = road[1];
long cost = road[2];
graph.putIfAbsent(from, new ArrayList<>());
graph.get(from).add(new long[]{to, cost});
graph.putIfAbsent(to, new ArrayList<>());
graph.get(to).add(new long[]{from, cost});
}
// bfs
// Queue<long[]> queue = new LinkedList<>();
PriorityQueue<long[]> queue = new PriorityQueue<>((o1, o2) -> o2[1] - o1[1] >= 0 ? -1 : 1);
long[][] dist = new long[n][2];
for (int i = 0; i < n; i++) {
dist[i][0] = Long.MAX_VALUE;
dist[i][1] = 0;
}
dist[0][0] = 0;
dist[0][1] = 1;
final int mod = (int)1e9 + 7;
queue.offer(new long[]{0, 0});
while (!queue.isEmpty()) {
long[] cur = queue.poll();
if (!graph.containsKey(cur[0])) continue;
for (long[] next : graph.get(cur[0])) {
int nextNode = (int) next[0];
int nextCost = (int) next[1];
if (nextCost + cur[1] < dist[nextNode][0]) {
dist[nextNode][1] = dist[(int) cur[0]][1];
dist[nextNode][0] = nextCost + cur[1];
queue.offer(new long[]{nextNode, dist[nextNode][0]});
} else if (nextCost + cur[1] == dist[nextNode][0]) {
dist[nextNode][1] += dist[(int) cur[0]][1];
dist[nextNode][1] %= mod;
// queue.offer(new long[]{nextNode, dist[nextNode][0]});
}
}
}
return (int) dist[n - 1][1];
}