最小環
阿新 • • 發佈:2020-07-06
最小環(Tarjan \(\star \))
- 旅遊區可以表示為一張由 \(n\) 個節點 \(m\) 條邊組成無向圖。我故地重遊,卻發現自己只想儘快地結束這次旅遊。
- 我從景區的出發點(即 \(1\) 號節點)出發,卻只想找出最短的一條迴路重新回到出發點,並且中途不重複經過任意一條邊。即:我想找出從出發點到出發點的小環。
Input
- 每個測試點有多組測試資料。
- 第一行有一個正整數\(T, (T≤ 10)\)表示資料組數。
- 接下來對於每組資料,第一行有兩個正整數 \(n,m, (n\le 10^4,m ≤ 4\times 10^4\)) 分別代表圖的點數和邊數。
- 接下來有\(m\)行,每行三個整數\(u,v,d\)
- 保證不存在重邊,自環。
Output
- 對於每組測試資料,輸出題目中所求的最小環的長度。
- 無解輸出 \(-1\)。
Sample Input
2
3 3
1 2 1
2 3 1
3 1 1
4 5
1 2 2
2 3 2
3 4 2
1 4 2
1 3 5
Sample Output
3
8
Hint
- 來源:\(hzoi2018\ NOIP\)模擬測試 \(6\) 《那一天她離我而去》
分析
- 我們發現我們可以把與 \(1\) 號節點相連的所有節點取出,如果我們把最小環在 \(1\) 號節點處斷開,那麼最小環斷成的鏈一定是以這些節點中的某一個節點作為起點,另一個節點作為終點的一條路路徑。
Code
#include <bits/stdc++.h> using namespace std; const int maxn=1e4+5,maxm=4e4+5; const int Inf=0x3f3f3f3f; struct Node{int to,next,w;}e[maxm<<1]; int n,m,ans; int head[maxn],len,dis[maxn]; bool vis[maxn]; priority_queue<pair<int, int> > q; void Insert(int u,int v,int w){ e[len].to=v;e[len].w=w;e[len].next=head[u];head[u]=len++; } void Spfa(int x){ memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); queue<int> q; q.push(x);dis[x]=0; while(!q.empty()){ int u=q.front();q.pop(); vis[u]=0; for(int i=head[u];~i;i=e[i].next){ int v=e[i].to; if(dis[v]>dis[u]+e[i].w){ dis[v]=dis[u]+e[i].w; if(!vis[v]){ q.push(v);vis[v]=1; } } } } } void Solve(){ int T;scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); memset(head,-1,sizeof(head)); len=0; for(int i=1,u,v,w;i<=m;i++){ scanf("%d%d%d",&u,&v,&w); Insert(u,v,w),Insert(v,u,w); } ans=Inf; for(int i=head[1];~i;i=e[i].next){ int temp=e[i].w; e[i].w=e[i^1].w=Inf;//斷掉一條鄰接邊 Spfa(1); ans=std::min(ans,dis[e[i].to]+temp); e[i].w=e[i^1].w=temp;//續上 } if(ans==Inf) ans=-1; printf("%d\n",ans); } } int main(){ Solve(); return 0; }