zoukankan      html  css  js  c++  java
  • 算法分析与实践-作业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

  • 相关阅读:
    关于relative和absolute的总结
    docker命令
    了解docker
    数据库性能监测指标(如Oracle、SqlServer)、LoadRunner 性能测试指标
    MySQL游标
    MySQL创建用户
    MySQL数据的操作
    MySQL创建数据库和表
    MySQL视图的操作
    MySQL数据备份与恢复
  • 原文地址:https://www.cnblogs.com/JayShao/p/12381830.html
Copyright © 2011-2022 走看看