牛客練習賽32 D Tarjan無向圖求橋+並查集維護
阿新 • • 發佈:2018-12-02
題目描述:
小p和他的朋友約定好去遊樂場遊玩,但是他們到了遊樂場後卻互相找不到對方了。
遊樂場可以看做是一張n個點,m條道路的圖,每條道路有邊權wi,表示第一次經過該道路時的花費(第二次及以後經過時花費為0)。
現在,小p要去找他的朋友,但他的朋友行蹤很詭異,小p總是要遍歷完這n個點才能找到他,同時小p希望總花費最小。
找到朋友的方案可能不唯一(具體看樣例解釋),小p想知道在這所有的方案中,有多少條邊在每個方案中都會被經過。
輸入描述:
第一行兩個整數n, m. p,分別表示點數,邊數,小p的初始位置。 接下來m行,每行兩個整數u, v, w表示從u到v有一條無向邊,邊權為w。
輸出描述:
輸出一個整數k,表示必須經過的邊的數量。
示例1
輸入
5 7 1 1 2 3 2 3 7 1 3 5 2 4 2 1 5 3 5 4 3 2 5 3
輸出
2
樣例解釋:
幾種可能的方案如下:
可以證明,4 - 2和1 - 3這兩條邊在所有方案中都被經過。
(以上每種方案的總花費均為13,同時可以證明沒有比這更優的策略)
示例2
輸入
3 3 1 1 2 1 1 3 1 2 3 2
輸出
2
示例3
輸入
3 3 1 1 2 2 2 3 2 1 3 2
輸出
0
備註:
保證圖聯通,保證無自環,保證無重邊
連結:https://ac.nowcoder.com/acm/contest/272/D
來源:牛客網
簡單來說就是尋找有幾條邊一定在最小生成樹上
將路徑按照權值排序後,按照權值由小到大的順序將點增加到最小生成樹中,使用並查集維護圖的連通性
如果路徑權值相等,路徑兩個端點已經在連通圖中,那麼不用新增這條邊
如果兩個端點不在連通圖中,那麼將邊新增到新圖中,然後使用Tarjan演算法求解出無向圖中的橋,即一定存在於最小生成樹中的邊
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <vector> using namespace std; typedef long long ll; const int maxn = 200100; int n,m,s; vector<pair<int,int> > v[maxn]; //v[x] 表示以x開始的節點,pair.first表示邊到達的節點,pair.second表示邊的編號 struct Edge { int u,v,w,id; bool operator<(const Edge&a)const { return w < a.w; } }edge[maxn]; int dfn[maxn],low[maxn],k,per[maxn],ans[maxn]; void tarjan(int x,int fa) //Tarjan求解圖中的橋 { dfn[x] = low[x] = ++k; for(int i = 0,to,id;i < v[x].size();i ++) { if((id = v[x][i].second) == fa) continue; //路徑返回了回去,不用考慮 to = v[x][i].first; if(!dfn[to]){ tarjan(to,id);low[x] = min(low[x],low[to]); if(low[to] > dfn[x]) ans[id] = 1,cout<<edge[id].u<<" "<<edge[id].v<<endl;; } else low[x] = min(dfn[to],low[x]); } } int find(int x) { return x == per[x]?per[x]:per[x] = find(per[x]); } int main() { int x,y,z; scanf("%d%d%d",&n,&m,&s); for(int i = 1; i <= n;i ++) per[i] = i; for(int i = 1;i <= m;i++) { scanf("%d%d%d",&x,&y,&z); edge[i] = (Edge){x,y,z,i}; } sort(edge+1,edge+1+m); int nxt; for(int i = 1;i <= m;i = nxt + 1) { nxt = i+1; for(; edge[i].w == edge[nxt].w; nxt++); nxt--; //[i-nxt]為路徑權值相等的區間範圍 for(int j = i;j <= nxt;j ++) //遍歷這個區間 { x = find(edge[j].u),y = find(edge[j].v); if(x == y) continue; //如果已經連通,則不需要考慮這條邊 v[x].push_back(make_pair(y,j)); //否則將這條邊新增到圖中 v[y].push_back(make_pair(x,j)); } for(int j = i;j <= nxt;j ++) { x = find(edge[j].u),y = find(edge[j].v); if(x == y || dfn[x]) continue; tarjan(x,-1); } for(int j = i;j <= nxt;j ++) { x = find(edge[j].u),y = find(edge[j].v); if(x == y) continue; v[x].clear();v[y].clear(); dfn[x] = 0;dfn[y] = 0; per[x] = y; } } int tot = 0; for(int i = 1;i <= m;i ++) tot += ans[i]; printf("%d\n",tot); return 0; }