LeetCode每日一题 847.访问所有节点的最短路径

545 阅读2分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

847. 访问所有节点的最短路径

存在一个由 n 个节点组成的无向连通图,图中的节点按从 0n - 1 编号。

给你一个数组 graph 表示这个图。其中,graph[i] 是一个列表,由所有与节点 i 直接相连的节点组成。

返回能够访问所有节点的最短路径的长度。你可以在任一节点开始和停止,也可以多次重访节点,并且可以重用边。

示例 1:

img

输入:graph = [[1,2,3],[0],[0],[0]]
输出:4
解释:一种可能的路径为 [1,0,2,0,3]

示例 2:

img

输入:graph = [[1],[0,2,4],[1,3,4],[2],[1,2]]
输出:4
解释:一种可能的路径为 [0,1,4,2,3]

提示:

  • n == graph.length
  • 1 <= n <= 12
  • 0 <= graph[i].length < n
  • graph[i] 不包含 i
  • 如果 graph[a] 包含 b ,那么 graph[b] 也包含 a
  • 输入的图总是连通图

方法一

状态压缩DP + BFS:这是一个求最短路问题,并且边权为1,那么首先想到用BFS来求解;题目中所求的是访问完所有节点的最短路,那么我们入队的状态应当是当前整个图的各个点是否被访问的一个整体状态;那么该用什么来表示这整体状态呢?看到提示中给出的数据范围,我们想到可以使用状态压缩来表示,即用0~(1 << n) - 1来表示,其中第i位为1,说明第i个节点已经被访问过;

class Solution {
    public int shortestPathLength(int[][] graph) {
        LinkedList<int[]> q = new LinkedList<>();
        int n = graph.length;
        //因为可以走访问过的点,且可以重用边,所以得开二维,记录当前在哪个点
        //f[i][j]表示,当前整个图的状态为i,且目前在停留在点j
        int[][] f = new int[1 << n][n];
        for (int i = 0; i < 1 << n; i ++ )
            for (int j = 0; j < n; j ++ )
                f[i][j] = (int)1e8;
​
        for (int i = 0; i < n; i ++ ) {
            q.add(new int[]{(1 << i), i});
            f[1 << i][i] = 0;
        }
​
        while(q.size() > 0) {
            int[] t = q.poll();
            for (int x : graph[t[1]]) {
                int s = t[0] | (1 << x);
                if (f[s][x] > f[t[0]][t[1]] + 1) {
                    f[s][x] = f[t[0]][t[1]] + 1;
                    q.add(new int[]{s, x});
                }
            }
        }
​
        int res = (int)1e8;
        for (int i = 0; i < n; i ++ )
            res = Math.min(res, f[(1 << n) - 1][i]);
        return res;
            
    }
}

时间复杂度: O(n*(1<<n))

空间复杂度: O(n*(1<<n))