题目链接
https://leetcode.cn/problems/shortest-path-with-alternating-colors/
解答过程
这个题目可是费了不少的时间和精力。从解题着手点来说,大的思路并不难,比较容易想到可以通过广度优先遍历(BFS)来解决这个问题。问题的关键在于每一层BFS具体做哪些操作?这可以从循环不变量来入手。我一开始的思路是每次进入循环时,当前维护着这样两个变量:shortestPathLength[],记录的是从0节点到v节点的最短颜色交错路径长度;lastPathColor[],记录的是从0节点到v节点的最短颜色交错路径的最后一条path的颜色,可能的取值有不可达、红色、蓝色、红蓝均可等四种情况。在当前遍历中,根据当前v节点的lastPathColor,决定是继续遍历redEdges或者blueEdges或者都遍历,同时更新遍历到的节点的shortestPath和lastPathColor。直到遍历结束,根据lastPathColor确定不可达的节点并更新shortestPathLength,然后返回。整个思路看起来还挺正确吧?处理了一些细节问题以后,最终的结果是超时,出现了死循环,因为可能出现环,对于已经遍历过的节点是否要二次遍历直觉上很难处理。
这个时候只能参考官方题解了。看了一下官方题解,大的思路是类似的,但有一个地方启发了我,那就是分别维护shortestPathLengthWithRedLastPath[]和shortestPathLengthWithBlueLastPath[],而不是妄图用一个lastPathColor[]来维护所有可能的状态。修改了一下代码,终于通过了,但是代码看起来属实有点复杂。
public int[] shortestAlternatingPaths(int n, int[][] redEdges, int[][] blueEdges) {
int[] shortestPathLengthWithRedLastPath = new int[n];
shortestPathLengthWithRedLastPath[0] = 0;
for (int i = 1; i < n; i++) {
shortestPathLengthWithRedLastPath[i] = -1;
}
int[] shortestPathLengthWithBlueLastPath = new int[n];
shortestPathLengthWithBlueLastPath[0] = 0;
for (int i = 1; i < n; i++) {
shortestPathLengthWithBlueLastPath[i] = -1;
}
Queue<Integer> queue = new LinkedList<>();
queue.offer(0);
while (!queue.isEmpty()) {
Set<Integer> nextRound = new HashSet<>();
int size = queue.size();
while (size-- > 0) {
int v = queue.poll();
if (shortestPathLengthWithBlueLastPath[v] != -1) {
int length = shortestPathLengthWithBlueLastPath[v] + 1;
for (int i = 0; i < redEdges.length; i++) {
if (redEdges[i][0] == v) {
int next = redEdges[i][1];
if (shortestPathLengthWithRedLastPath[next] == -1) {
shortestPathLengthWithRedLastPath[next] = length;
nextRound.add(next);
} else {
if (length < shortestPathLengthWithRedLastPath[next]) {
shortestPathLengthWithRedLastPath[next] = length;
}
}
}
}
}
if (shortestPathLengthWithRedLastPath[v] != -1) {
int length = shortestPathLengthWithRedLastPath[v] + 1;
for (int i = 0; i < blueEdges.length; i++) {
if (blueEdges[i][0] == v) {
int next = blueEdges[i][1];
if (shortestPathLengthWithBlueLastPath[next] == -1) {
shortestPathLengthWithBlueLastPath[next] = length;
nextRound.add(next);
} else {
if (length < shortestPathLengthWithBlueLastPath[next]) {
shortestPathLengthWithBlueLastPath[next] = length;
}
}
}
}
}
}
queue.addAll(nextRound);
}
int[] shortestPathLength = new int[n];
for (int i = 0; i < n; i++) {
int withRedLastPath = shortestPathLengthWithRedLastPath[i];
int withBlueLastPath = shortestPathLengthWithBlueLastPath[i];
if (withRedLastPath == -1
&& withBlueLastPath == -1) {
shortestPathLength[i] = -1;
} else if (withRedLastPath == -1) {
shortestPathLength[i] = withBlueLastPath;
} else if (withBlueLastPath == -1) {
shortestPathLength[i] = withRedLastPath;
} else {
shortestPathLength[i] = Math.min(withRedLastPath, withBlueLastPath);
}
}
return shortestPathLength;
}
又看了一些官方题解的代码,额,看不懂,虽然是看着简洁一些,但感觉还不如我自己写的好理解。