1. 程式人生 > >(模板)最小生成樹

(模板)最小生成樹

證明 依次 找到 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 int
x=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 }

(模板)最小生成樹