(模板)最小生成樹
阿新 • • 發佈:2019-01-31
證明 依次 找到 node void mic 端點 round size
2019-01-30
最小生成樹基本算法
定義:
給定一個邊帶權的無向圖G=(V,E),n=|V|,m=|E|,由V中全部n個定點和E中n-1條邊構成的無向連通子圖被稱為G的一顆生成樹。
邊的權值之和最小的生成樹被稱為無向圖G的最小生成樹。(Minimun Spanning Tree,MST).
定理:
任意一顆最小生成樹一定包含無向圖中權值最小的邊
證明:
假設最小的邊z不在MST上,將其加入樹中,可構成一個環,並且環上所有邊權都比z大,因此用z代表任意一條邊,所得的生成樹都一定會比原來更小。假設不成立。
Kruskal:
案邊權排序,然後依次掃描每個邊(x,y,z),若x,y屬於同一個集合,則忽略這條邊,否則合並x,y所在的集合,將z累加到答案中。---O(mlogm)
代碼:
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 struct rec{ 6 int x,y,z; 7 }e[200010]; 8 9 int cmp(rec a,rec b){ 10 return a.z<b.z; 11 } 12 int fa[5010],n,m,ans=0; 13 14 int get(int x){ 15 if(x==fa[x]) return x; 16 return fa[x]=get(fa[x]); 17 } 18 19 int main(){ 20 scanf("%d%d",&n,&m); 21 for(int i=1 ; i<=m ; i++) 22 scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].z); 23 sort(e+1,e+1+m,cmp); 24 for(int i=1 ; i<=n ; i++) fa[i]=i; 25 26 for(int i=1 ; i<=m ; i++){ 27 intx=get(e[i].x); 28 int y=get(e[i].y); 29 if(x==y) continue; 30 fa[x]=y; 31 ans+=e[i].z; 32 } 33 printf("%d\n",ans); 34 return 0; 35 }
Prim:
任一時刻,設已經確定屬於最小生成樹的節點集合為T,剩余點集合為S,找到minx€S,y€T 即兩個端點分別屬於S和T的權值最小的邊,然後把點x從集合S中刪除,加入集合T,並把該邊邊權累加到答案中。
具體來說,就是一個維護數組d[x]: 當x未被選中時,表示x與S中的節點間權值最小的邊的權值。 若x已被選中,表示 x被選中加入已選集合時選中的最小邊的權值。
遍歷1~n-1每個點,每次選出T中d[]最小的點加入集合S,然後更新T中其它點的d值---O(n^2),
代碼:
1 const int N=3010; 2 int a[N][N],d[N],n,m,ans; 3 bool v[N]; 4 5 void prim() 6 { 7 memset(d,0x3f,sizeof(d)); 8 memset(v,0,sizeof(v)); 9 d[1]=0; 10 for(int i=1 ; i<n ; i++) 11 { 12 int x(0); 13 for(int j=1 ; j<=n ; j++) 14 if(!v[j]&&d[j]<d[x]) x=j; 15 v[x]=1; 16 for(int y=1 ; y<=n ; y++) 17 if(!v[y]) 18 d[y]=min(d[y],d[x]+a[x][y]); 19 } 20 } 21 22 int main() 23 { 24 prim(); 25 for(int i=2 ; i<=n ; i++) ans+=d[i]; 26 }
Prim優先隊列優化---O(mlogn)
1 #include<bits/stdc++.h> 2 #define INF 0x7f7ff 3 using namespace std; 4 int n,m,dist[5010],head[5010],k,ans,tot; 5 bool vis[5010]; 6 struct node 7 { 8 int to,next,w; 9 }edge[400010]; 10 struct p 11 { 12 int id,d; 13 bool operator < (const p &a) const 14 { 15 return a.d<d; 16 } 17 }; 18 void add(int u,int v,int w) 19 { 20 edge[++k].to=v; 21 edge[k].w=w; 22 edge[k].next=head[u]; 23 head[u]=k; 24 } 25 void Prim() 26 { 27 fill(dist+1,dist+1+n,INF); 28 priority_queue<p> q; 29 p now; 30 now.id=1;now.d=dist[1]=0; 31 q.push(now); 32 while(!q.empty()) 33 { 34 p now=q.top();q.pop(); 35 int u=now.id; 36 if(now.d!=dist[u]) continue; 37 vis[u]=1; 38 ans+=dist[u]; 39 tot++; 40 for(int i=head[u];i;i=edge[i].next) 41 { 42 int v=edge[i].to; 43 if(!vis[v]&&dist[v]>edge[i].w) 44 { 45 dist[v]=edge[i].w; 46 p nxt; 47 nxt.d=dist[v]; 48 nxt.id=v; 49 q.push(nxt); 50 } 51 } 52 } 53 if(tot<n) ans=-1; 54 } 55 int main() 56 { 57 // std::ios::sync_with_stdio(false); 58 cin>>n>>m; 59 for(int i=1;i<=m;i++) 60 { 61 int a,b,c; 62 cin>>a>>b>>c; 63 add(a,b,c); 64 add(b,a,c); 65 } 66 Prim(); 67 if(ans==-1) cout<<"orz"<<endl; 68 else cout<<ans; 69 }
(模板)最小生成樹