最小生成树

  最小生成树的定义:一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。

  定理:任意一棵最小生成树一定包含无向图中权值最小的边。

  在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此的权重,若存在 T 为 E 的子集(即)且为无循环图,使得的 w(T) 最小,则此 T 为 G 的最小生成树。
   
 
  最小生成树其实是最小权重生成树的简称。
  下面介绍两种算法:Kruskal和Prim。
 Kruskal是维护无向图的最小生成森林。Prim是维护最小生成树的一部分。
  Kruskal:
  1.建立并查集,以每个点为一个集合。
  2.边权从小到大排序,扫描每条边(横坐标,纵坐标,边权)(下文用x,y,z分别表示)。
  3.若x,y属于同一集合(就是所谓的联通),跳过,下一条。
  4.如果不是,将其合并(这时候就用到了并查集),把z累加到答案中。
  5.处理完所有边后,上一步处理的边就是最小生成树。
  复杂度(o(m log m));
  核心代码:
  
#include<iostream>
#include<cstdio>
#include<algorithm>

using namespace std;

struct rec{
    int x,y,z;
}edge[500010];

int fa[100010],n,m,ans;

bool operator <(rec a,rec b){
    return a.z<b.z;
}

int get(int x){
    if(x==fa[x]) return x;
    return fa[x]=get(fa[x]);
}

int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z);
    sort(edge+1,edge+m+1);//按边权排序 
    for(int i=1;i<=n;i++) fa[i]=i;//并查集初始化 
    for(int i=1;i<=m;i++){
        int x=get(edge[i].x);
        int y=get(edge[i].y);
        if(x==y) continue;
        fa[x]=y;
        ans+=edge[i].z; 
    }//求最小生成树 
    cout<<ans<<endl;
}

 

  Prim:

  最初,仅先确定1号节点属于最小生成树。任意时刻假设已经确定属于最小生成树的节点集合为T,剩余节点集合为S。Prim算法找到,即两个端点分别属于集合S,T的权值最小的边,然后把点x从集合S中删除,加入到集合T,并把z累加到答案中。

  具体一点,维护数组d:若x∈S,则d[x]表示节点x与集合T的节点之间权值最小的边的权值。若x∈T,则d[x]就等于x被加入T时选出的最小值的权值。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

int a[3010][3010],d[3010],n,m,ans;
bool v[3010];

void prim(){
    memset(d,0x3f,sizeof(d));
    memset(v,0,sizeof(v));
    d[1]=0;
    for(int i=1;i<n;i++){
        int x=0;
        for(int j=1;j<=n;j++){
            if(!v[j]&&(x==0||d[j]<d[x])) x=j;
        v[x]=1;
        for(int y=1;y<=n;y++)
            if(!v[y]) d[y]=min(d[y],a[x][y]);
        }
    }
}

int main(){
    cin>>n>>m;
    memset(a,0x3f,sizeof(a));
    for(int i=1;i<=n;i++) a[i][i]=0;
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        a[y][x]=a[x][y]=min(a[x][y],z);
    }//建立邻接矩阵 
    prim();
    for(int i=2;i<=n;i++) ans+=d[i];//求最小生成树 
    cout<<ans<<endl;
}

两种方法不做比较,因题和个人而定。

posted @ 2020-07-13 17:01  国服混子瑶  阅读(262)  评论(0编辑  收藏  举报