classSolution: defshortestPathLength(self, graph: List[List[int]]) -> int: n = len(graph) q = deque((i, 1 << i, 0) for i inrange(n)) seen = {(i, 1 << i) for i inrange(n)} ans = 0 while q: u, mask, dist = q.popleft() if mask == (1 << n) - 1: ans = dist break # 搜索相邻的节点 for v in graph[u]: # 将 mask 的第 v 位置为 1 mask_v = mask | (1 << v) if (v, mask_v) notin seen: q.append((v, mask_v, dist + 1)) seen.add((v, mask_v)) return ans
funcshortestPathLength(graph [][]int)int { n := len(graph) type tuple struct{ u, mask, dist int } q := []tuple{} seen := make([][]bool, n) for i := range seen { seen[i] = make([]bool, 1<<n) seen[i][1<<i] = true q = append(q, tuple{i, 1 << i, 0}) }
for { t := q[0] q = q[1:] if t.mask == 1<<n-1 { return t.dist } // 搜索相邻的节点 for _, v := range graph[t.u] { maskV := t.mask | 1<<v if !seen[v][maskV] { q = append(q, tuple{v, maskV, t.dist + 1}) seen[v][maskV] = true } } } }
intshortestPathLength(int** graph, int graphSize, int* graphColSize){ int n = graphSize; structNodeq[n * (1 << n)]; int left = 0, right = 0; int seen[n][1 << n]; memset(seen, 0, sizeof(seen)); for (int i = 0; i < n; ++i) { q[right++] = (struct Node){i, 1 << i, 0}; seen[i][1 << i] = true; }
int ans = 0; while (left < right) { int u = q[left].id; int mask = q[left].mask; int dist = q[left++].dist; if (mask == (1 << n) - 1) { ans = dist; break; } // 搜索相邻的节点 for (int i = 0; i < graphColSize[u]; i++) { int v = graph[u][i]; // 将 mask 的第 v 位置为 1 int mask_v = mask | (1 << v); if (!seen[v][mask_v]) { q[right++] = (struct Node){v, mask_v, dist + 1}; seen[v][mask_v] = true; } } } return ans; }
复杂度分析
时间复杂度:O(n^2 \cdot 2^n)。常规的广度优先搜索的时间复杂度为 O(n + m),其中 n 和 m 分别表示图的节点数和边数。本题中引入了 mask 这一维度,其取值范围为 [0, 2^n),因此可以看成是进行了 2^n 次常规的广度优先搜索。由于 m 的范围没有显式给出,在最坏情况下为完全图,有 m = O(n^2),因此总时间复杂度为 O(n^2 \cdot 2^n)。
空间复杂度:O(n \cdot 2^n),即为队列需要使用的空间。
方法二:预处理点对间最短路 + 状态压缩动态规划
思路与算法
由于题目中给定的图是连通图,那么我们可以计算出任意两个节点之间 u, v 间的最短距离,记为 d(u, v)。这样一来,我们就可以使用动态规划的方法计算出最短路径。
对于任意一条经过所有节点的路径,它的某一个子序列(可以不连续)一定是 0, 1, \cdots, n - 1 的一个排列。我们称这个子序列上的节点为「关键节点」。在动态规划的过程中,我们也是通过枚举「关键节点」进行状态转移的。
我们用 f[u][\textit{mask}] 表示从任一节点开始到节点 u 为止,并且经过的「关键节点」对应的二进制表示为 mask 时的最短路径长度。由于 u 是最后一个「关键节点」,那么在进行状态转移时,我们可以枚举上一个「关键节点」v,即:
publicclassSolution { publicintShortestPathLength(int[][] graph) { int n = graph.Length; int[,] d = newint[n, n]; for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { d[i, j] = n + 1; } } for (int i = 0; i < n; ++i) { foreach (int j in graph[i]) { d[i, j] = 1; } } // 使用 floyd 算法预处理出所有点对之间的最短路径长度 for (int k = 0; k < n; ++k) { for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { d[i, j] = Math.Min(d[i, j], d[i, k] + d[k, j]); } } }
int[,] f = newint[n, 1 << n]; for (int i = 0; i < n; ++i) { for (int j = 0; j < 1 << n; ++j) { f[i, j] = int.MaxValue / 2; } } for (int mask = 1; mask < (1 << n); ++mask) { // 如果 mask 只包含一个 1,即 mask 是 2 的幂 if ((mask & (mask - 1)) == 0) { int u = BitCount((mask & (-mask)) - 1); f[u, mask] = 0; } else { for (int u = 0; u < n; ++u) { if ((mask & (1 << u)) != 0) { for (int v = 0; v < n; ++v) { if ((mask & (1 << v)) != 0 && u != v) { f[u, mask] = Math.Min(f[u, mask], f[v, mask ^ (1 << u)] + d[v, u]); } } } } } }
int ans = int.MaxValue; for (int u = 0; u < n; ++u) { ans = Math.Min(ans, f[u, (1 << n) - 1]); } return ans; }
publicstaticintBitCount(int i) { i = i - ((i >> 1) & 0x55555555); i = (i & 0x33333333) + ((i >> 2) & 0x33333333); i = (i + (i >> 4)) & 0x0f0f0f0f; i = i + (i >> 8); i = i + (i >> 16); return i & 0x3f; } }
classSolution: defshortestPathLength(self, graph: List[List[int]]) -> int: n = len(graph) d = [[n + 1] * n for _ inrange(n)] for i inrange(n): for j in graph[i]: d[i][j] = 1 # 使用 floyd 算法预处理出所有点对之间的最短路径长度 for k inrange(n): for i inrange(n): for j inrange(n): d[i][j] = min(d[i][j], d[i][k] + d[k][j])
f = [[float("inf")] * (1 << n) for _ inrange(n)] for mask inrange(1, 1 << n): # 如果 mask 只包含一个 1,即 mask 是 2 的幂 if (mask & (mask - 1)) == 0: u = bin(mask).count("0") - 1 f[u][mask] = 0 else: for u inrange(n): if mask & (1 << u): for v inrange(n): if (mask & (1 << v)) and u != v: f[u][mask] = min(f[u][mask], f[v][mask ^ (1 << u)] + d[v][u])
ans = min(f[u][(1 << n) - 1] for u inrange(n)) return ans
funcshortestPathLength(graph [][]int)int { n := len(graph) d := make([][]int, n) for i := range d { d[i] = make([]int, n) for j := range d[i] { d[i][j] = n + 1 } } for v, nodes := range graph { for _, u := range nodes { d[v][u] = 1 } }
// 使用 floyd 算法预处理出所有点对之间的最短路径长度 for k := range d { for i := range d { for j := range d { d[i][j] = min(d[i][j], d[i][k]+d[k][j]) } } }
f := make([][]int, n) for i := range f { f[i] = make([]int, 1<<n) for j := range f[i] { f[i][j] = math.MaxInt64 / 2 } } for mask := 1; mask < 1<<n; mask++ { // 如果 mask 只包含一个 1,即 mask 是 2 的幂 if mask&(mask-1) == 0 { i := bits.TrailingZeros(uint(mask)) f[i][1<<i] = 0 continue } for u := 0; u < n; u++ { if mask>>u&1 > 0 { for v := 0; v < n; v++ { if v != u && mask>>v&1 > 0 { f[u][mask] = min(f[u][mask], f[v][mask^(1<<u)]+d[v][u]) } } } } } ans := math.MaxInt64 for u := 0; u < n; u++ { ans = min(ans, f[u][1<<n-1]) } return ans }
funcmin(a, b int)int { if a < b { return a } return b }