算法少女 2018-02-16
我们所写的最小生成树主要有两种,分别是Prime和Kluskal。
注:n表示节点数,m表示边数。
Prime算发是一种对点的枚举算法,算法实现的实质是贪心,即每次加入的点都是离已知生成树部分最近的点,加入生成树一个新的点的同时更新还未加入的点与生成树的最小距离,所以整个时间复杂度是在O(n2)的级别,这个算法在利用堆(或优先队列)优化后可以降到O(nlogn)的级别,一般用于稠密图和完全图。
1 struct Node{ 2 int pos,val; 3 bool operator < (const Node & a) const {return a.val<val;} 4 }; 5 priority_queue<Node> q; 6 int dis[105]; 7 void Prime(int s) 8 { 9 Node x,y; 10 dis[s]=0; 11 x.val=dis[s];x.pos=s; 12 q.push(x); 13 do{ 14 x=q.top();q.pop(); 15 int u=x.pos; 16 sum+=dis[u]; 17 dis[u]=0; 18 for(int i=1;i<=n;i++) 19 { 20 if(Map[u][i]+dis[u]<dis[i]) 21 { 22 dis[i]=Map[u][i]+dis[u]; 23 y.val=dis[i]; 24 y.pos=i; 25 q.push(y); 26 } 27 } 28 }while(!q.empty()); 29 }
Kluskal算法是求最小生成树的另一种算法,同样是利用了贪心的思想。我们先把所有的点看作是以自己为根的一颗树,然后再把权值最小的两个树给合并起来直到所有的点都在一棵树上的时候,即连了(n-1)条边之后。这种算法最主要的就是合并操作的实现,很容易就可以想到用并查集这种数据结构来实现,所以这整个算法的复杂度就是O(mlogm)的排序操作和O(1)的合并操作,我们可以很轻松的发现这种算法运用在稀疏图上是十分方便的。
1 #include<bits/stdc++.h> 2 using namespace std; 3 struct Edge{ 4 int fr,to,cos; 5 }edge[10005]; 6 int fa[2005]; 7 int find(int x) 8 { 9 return x==fa[x]?x:fa[x]=find(fa[x]); 10 } 11 bool comp(const Edge & a,const Edge & b) 12 { 13 return a.cos<b.cos; 14 } 15 int main() 16 { 17 int n,m,p,u,v,w; 18 int sum=0,k=0; 19 scanf("%d%d",&n,&m); 20 for(int i=1;i<=n;i++) fa[i]=i; 21 for(int i=1;i<=m;i++) 22 { 23 scanf("%d%d%d",&u,&v,&w); 24 edge[i].fr=u; 25 edge[i].to=v; 26 edge[i].cos=w; 27 } 28 sort(edge+1,edge+(m+1),comp); 29 if(k<=n-1) 30 { 31 for(int i=1;i<=m;i++) 32 { 33 u=edge[i].fr;v=edge[i].to;w=edge[i].cos; 34 u=find(u);v=find(v); 35 if(u!=v) 36 { 37 fa[v]=u; 38 sum+=w; 39 k++; 40 if(k==n-1) break; 41 } 42 } 43 } 44 printf("%d\n",sum); 45 return 0; 46 }
一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。