存在一个由 n 个节点组成的无向连通图,图中的节点按从 0 到 n - 1 编号。
给你一个数组 graph 表示这个图。其中,graph[i] 是一个列表,由所有与节点 i 直接相连的节点组成。
返回能够访问所有节点的最短路径的长度。你可以在任一节点开始和停止,也可以多次重访节点,并且可以重用边。
示例 1:
输入:graph = [[1,2,3],[0],[0],[0]]
输出:4
解释:一种可能的路径为 [1,0,2,0,3]
示例 2:
输入: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
输入的图总是连通图
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shortest-path-visiting-all-nodes
解法:
bfs+状态压缩。本题我是真的想不到可以这么做,看了官方题解后才自己去编写代码,其实思路还是好理解的。本题是无向图,且边的权重是1,求最短路径,所以可以用bfs。常规的bfs中队列存储的是节点的编号,本题要求的是经过全部节点的最短路径(本以为用最小生成树算法呢),所以还需存储已经经过的节点。最终用三元组(u, mask, dist)来存储信息:
1.u表示当前节点
2.mask是一个n为二进制数,表示经过了哪些节点,其中第i位为1,则表示第i个节点已经被搜索过。
3.dist表示遍历到节点u并且已经遍历过的节点状态为mask时,走过的距离。
同时需要保证每个节点u以及搜过情况mask只被搜索一次,所以需要一个二维数组state,state[u][mask]=true表示节点u以及搜过情况mask已经搜索过了。
class Solution {
public:
int shortestPathLength(vector<vector<int>>& graph) {
queue<tuple<int, int, int>> q; //tuple(u, mask, dist)表示搜索到节点u时,已经经过了的点的状态为mask,
//走过的距离为dist,其中mask是长度为n的二进制数,第i位为1,表示节点i
//已经遍历过
int n = graph.size();
if (n == 1)
return 0;
int top = 1 << n;
vector<vector<bool>> state(n, vector<bool>(top)); //state[u][mask]=true, 表示遍历到u节点时,
//已经遍历过的节点状态为mask
for (int i = 0; i < n; ++i)
{
q.push(make_tuple(i, 1 << i, 0)); //可以从任一节点开始,初始入队所有节点
state[i][1 << i] = true;
}
int ans = 0;
bool flag = false;
while (!q.empty())
{
int u, mask, dist;
tie( u, mask, dist) = q.front(); //或者用auto [u, mask, dist]=q.front()
q.pop();
for (auto &v : graph[u])
{
int mask_v = mask | (1 << v);
if (!state[v][mask_v])
{
state[v][mask_v] = true;
q.push(make_tuple(v, mask_v, dist + 1));
if (mask_v == top - 1) //终止状态
{
ans = dist+1;
flag = true;
break;
}
}
}
if (flag)
break;
}
return ans;
}
};
本题的时间复杂度为O(),空间复杂对O(n
)