题目链接:https://leetcode.com/problems/shortest-path-visiting-all-nodes/
题意:已知一条无向图,问经过所有点的最短路径是多长,边权都为1,每个点可能经过多次。
这道题写的时候想简单了,把它当成树的直径来做了,求出一条最长路径len(len上的点只经过一次),2*(点数-1)-len即为答案,竟然过了,后来看了看讨论区发现这不是正解,而且我也没办法证明,感觉是蒙对的。贴下代码:
class Solution { public: void dfs(bool &f,int x, int sum, vector<vector<int>>& graph, int &ans, int vis[]) { if (f) return ; if (sum == graph.size()-1) { //不加这个剪枝还会TLE,加了以后快的飞起,因为存在全连接图 f = 1; ans = sum; return ; } vis[x] = 1; for (int i = 0;i < graph[x].size();i++) { if (vis[graph[x][i]] == 0) { dfs( f,graph[x][i], sum + 1, graph, ans, vis); vis[graph[x][i]] = 0; } } ans = max(sum, ans); //ans为图中的最长路径(路径上的点只经过一次) } int shortestPathLength(vector<vector<int>>& graph) { if (graph[0].size() == 0) return 0; int vis[15]; memset(vis, 0, sizeof(vis)); int ans = 0; bool f = 0; for (int i = graph.size()-1;i>=0&&!f;i--) { memset(vis, 0, sizeof(vis)); dfs(f,i, 0, graph, ans, vis); } cout << ans << -1 << endl; return (graph.size() - 1) * 2 - ans; } };
正解应该是状压dp:
dp[i][j]表示当前在第i个节点,且已经走过的节点集合为j用二进制表示时1的位置,从u到v,$dp[v][j|(1<<v)] = min(dp[v][j],dp[u][j]+1);$(dp[u][j]为在u点的状态),bfs不断更新即可
class Solution { public: int shortestPathLength(vector<vector<int>>& graph) { vector<vector<int> > dp(graph.size(),vector<int>((1<<graph.size()),1e9) ); queue<pair<int,int> > q; //使用队列存储dp值 for(int i=0;i<graph.size();i++){ dp[i][1<<i]=0; q.push(make_pair(i,1<<i)); } while(!q.empty()){ pair p = q.front(); q.pop(); for(int i=0;i<graph[p.first].size();i++){ int v=graph[p.first][i]; if(dp[v][p.second|(1<<v)]>dp[p.first][p.second]+1){ //比当前优则入队 dp[v][p.second|(1<<v)] = dp[p.first][p.second]+1; q.push(make_pair(v,p.second|(1<<v))); } } } int ans=1e9; for(int i=0;i<graph.size();i++) ans=min(ans,dp[i][(1<<graph.size())-1]); return ans; } };