1. 程式人生 > >並查集(按秩合併)

並查集(按秩合併)

並查集-按秩合併

題目:UVA-11354

題目大意:給出一張n個點m條邊的無向圖, 每條邊有一個危險度,有q個詢問, 每次給出兩個點s、t,找一條路, 使得路徑上的最大危險度最小。

思路:首先,我們可以發現,如果求一個最小生成樹, 那麼任意兩點, 在生成樹上有唯一路徑, 而且這條路徑上的最大危險值一定最小。 但是n和q都太大, 如果直接順著樹走,每次詢問最大複雜度O(n), 那麼複雜度高達O(n^2),會超時。 我們知道, 並查集在用了路徑壓縮之後效率高達O(n), 但是卻破壞了樹形結構, 所以不能用路徑壓縮。 然而我們經常忽視了按秩合併這個方法, 即使不用路徑壓縮, 僅僅靠按秩合併, 複雜度也可低至O(logn)。 因此我們只需按秩合併, 然後詢問的時候向根回溯就行了, 複雜度mlogn。

注意秩的意思就是樹的高度,按秩合併過後並查集的結構為樹形結構

樣例:

4 5
1 2 10
1 3 20
1 4 100
2 4 30
3 4 10
2
1 4
4 1

此樣例按秩合併過後的結構如下:

這裡寫圖片描述

可以看出來按秩合併過後,此並查集呈現出樹形的結構,並且呈遞增,所此每次訪問的肯定是從小到大的線路,因此路徑上的最大危險度肯定是最小的。。。

圖手畫的,不是很好看,還請見諒。。。。。。。

程式碼如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio> #include <cmath> #include <vector> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; int n,m; int pre[50000+10],ra[50000+10],vis[50000+10],e[50000+10]; struct node { int u,v,w; }p[100000+10]; bool cmp(node x,node y) { return x.w<y.w; } int Find(int x) { return
pre[x]==x?x:Find(pre[x]); } void Union(int a,int b,int c) { int x=Find(a); int y=Find(b); if(x==y) return ; if(ra[x]<ra[y]) { pre[x]=y; e[x]=c;//e陣列記錄路徑上的大小 } else { pre[y]=x; e[y]=c; if(ra[x]==ra[y]) ra[x]++;//ra陣列就是記錄樹的高度,也就是所謂的秩 } } int qurey(int x,int y)//查詢 { int ans1=0,ans2=-1; int cnt=x;//先從x往y查詢, while(true) { vis[cnt]=ans1; if(cnt==pre[cnt]) break; ans1=max(ans1,e[cnt]); cnt=pre[cnt]; } cnt=y; while(true) { if(vis[cnt]>=0)//如果剛剛x在y下面,那麼就能掃到y,這裡判斷一下是的話就可以直接返回了, { ans2=max(ans2,vis[cnt]); break; } if(cnt==pre[cnt]) break; ans2=max(ans2,e[cnt]); cnt=pre[cnt]; } cnt=x; while(true)//如果x是在y上面,那剛剛x以上的樹節點的vis值就被改變了,需要復原,以便下一次的查詢, { vis[cnt]=-1; if(cnt==pre[cnt]) break; cnt=pre[cnt]; } return ans2; } void init() { sort(p,p+m,cmp); for(int i=1;i<=n;i++) { pre[i]=i; ra[i]=0; vis[i]=-1; } for(int i=0;i<m;i++) { Union(p[i].u,p[i].v,p[i].w); } //cout<<"pre[4]="<<pre[4]<<" "<<"pre[3]="<<pre[3]<<endl; } int main() { int kase=0; while(cin>>n>>m) { if(kase) cout<<endl; else kase++; for(int i=0;i<m;i++) cin>>p[i].u>>p[i].v>>p[i].w; init(); int q; cin>>q; for(int i=0;i<q;i++) { int u,v; cin>>u>>v; cout<<qurey(u,v)<<endl; } } return 0; }