算法分析与实践-作业1

 最小生成树

1. 问题

        在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集(即)且为无循环图,使得的 w(T) 最小,则此 T 为 G 的最小生成树。

      

 

2. 解析

1.Prim算法:

        1).初始化:Vnew= {x},其中x为集合V中的任一节点(起始点),Enew= {},为空;

        2).重复下列操作,直到Vnew= V:

                a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);

                b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;

2.kruscal算法:

       先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有 n 棵树的一个森林。之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。

3. 设计

使用优先队列优化的prim算法:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=100000+10;
 4 const int maxm=100000+10;
 5 int n,m;
 6 int vis[maxn];        //用来判断该点是否在最小生成树中 
 7 struct node{          //存边的信息 
 8     int to,dis;
 9     bool operator <(const node &a)const{
10         return dis>a.dis;
11     }
12 };
13 vector<node>e[maxn];             //用vector容器来存边 
14 priority_queue<node>q;           //优先队列优先选择边权最小的边 
15 /*----------以下为prim算法----------*/ 
16 void prim(){
17     int ans=0;                   //表明最小生成树的权值 
18     int cnt=0;                   //表明在最小生成树中的点的数量 
19     q.push((node){1,0});
20     while(!q.empty()&&cnt<n){
21         node k=q.top();q.pop();
22         if(vis[k.to])continue;   //表明该点已经在最小生成树中 
23         vis[k.to]=1;
24         ans+=k.dis;
25         cnt++;
26         for(int i=0;i<e[k.to].size();++i){
27             if(!vis[e[k.to][i].to]){
28                 q.push((node){e[k.to][i].to,e[k.to][i].dis});
29             }
30         }
31     }
32     printf("%d
",cnt==n?ans:-1);
33 }
34 /*----------以上为prim算法----------*/ 
35 void init(){                    //初始化 
36     while(!q.empty())q.pop();
37     for(int i=1;i<=n;++i)e[i].clear(),vis[i]=0;
38 }
39 int main(){
40     while(scanf("%d %d",&n,&m)!=EOF){    //n代表点的数量,m代表边的数量 
41         init();
42         for(int i=1;i<=m;++i){
43             int x,y,z;
44             scanf("%d %d %d",&x,&y,&z);
45             e[x].push_back((node){y,z});
46             e[y].push_back((node){x,z});
47         }
48         prim();
49     }
50     return 0;
51 }

kruscal算法:

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn=100000+10;
 5 const int maxm=100000+10;
 6 int n,m;
 7 struct edge{
 8     int from,to,cost;
 9 }e[maxm];
10 bool cmp(struct edge &a,struct edge &b){
11     return a.cost<b.cost;
12 }
13 /*----------以下为通过并查集维护联通快----------*/
14 int fa[maxn];
15 int find(int x){
16     return fa[x]==x?fa[x]:fa[x]=find(fa[x]);
17 }
18 void baba(int x,int y){
19     int fx=find(x),fy=find(y);
20     fa[fx]=fy;
21 }
22 /*----------以上为通过并查集维护联通快----------*/
23 /*----------以下为kruskal----------*/
24 int kruskal(){
25     int cnt=1;                     //表明包含在最小生成树内的点的数量 
26     int res=0;                     //表明最小生成树的边权和 
27     sort(e+1,e+1+m,cmp);           //将每条边按照权值从小到大进行排序 
28     for(int i=1;i<=m;++i){
29         int fx=find(e[i].from),fy=find(e[i].to);
30         if(fx==fy)continue;        //若在同一联通块内,则调过本次循环 
31         baba(e[i].from,e[i].to);   //联通e[i].from和e[i].to 
32         cnt++;
33         res+=e[i].cost;
34         if(cnt==n)break;
35     }
36     return cnt==n?res:-1;   //若cnt不等于n,则表明无法生成最小生成树,输出-1 
37 }
38 /*----------以上为kruskal----------*/
39 void init(){  //初始化 
40     for(int i=1;i<=n;++i)fa[i]=i;
41 }
42 int main(){
43     while(scanf("%d %d",&n,&m)!=EOF){   //n表示点的数量,m表示边的数量 
44         init();
45         for(int i=1;i<=m;++i){
46             scanf("%d %d %d",&e[i].from,&e[i].to,&e[i].cost);
47         }
48         printf("%d
",kruskal());
49     }
50 }

4. 分析

//n为点的数量,m为边的数量

使用优先队列的Prim复杂度为:mlogn

Kruscal算符复杂度为:mlogm

5. 源码

Kruscal:https://github.com/JayShao-Xie/algorithm-work/blob/master/kruskal.cpp

Prim:https://github.com/JayShao-Xie/algorithm-work/blob/master/prim.cpp

原文地址:https://www.cnblogs.com/JayShao/p/12381830.html