最小生成树的两种方法

来源:互联网 发布:hadoop2.4.1源码下载 编辑:程序博客网 时间:2024/04/27 14:57

最小生成树问题的讲道理都是很直观的,让人一眼看得穿,然后再在模版上稍加改动就完成了。

一般来说Prim跟Kruskal方法都是能解题的。但是复杂度不一样。

Prim的时间复杂度是O(n^2),而Kruskal方法与给出的边的数量有关,复杂度为O(eloge)其实时间都是花在排序上了 。

1.Prim算法

简单来说,就是从一个点开始不断寻找,链接着其他相关点,每次加入一个距离最小的点。

其中low数组就是保存到其他点的距离,vis保证每个点只经过一次。

另外求最大生成树类似,只要将输入的数加上负号,最后输出的时候再加上负号。

#include<iostream>#include<string.h>using namespace std;#define N 105#define inf 0x3f3f3f3fint low[N],vis[N],G[N][N];int x,n,ans;int prim(){    int pos,minnum,result=0;    memset(vis,0,sizeof(vis));    vis[1]=1;pos=1;//从第一个定点开始也可从指定定点开始    for(int i=1; i<=n; i++)    {        if(i!=pos)low[i]=G[pos][i];}//更新low数组    for(int i=1; i<n; i++)//再运行n-1次    {        minnum=inf;        for(int j=1; j<=n; j++)        {            if(vis[j]==0&&minnum>low[j])            {                minnum=low[j];                pos=j;            }        }//if(minnum==inf)break;前面在加cnt变量,可以判断是否能成功生成最小生成树        result+=minnum;//找到最小值与下一点        vis[pos]=1;        for(int i=1; i<=n; i++)        {            if(vis[i]==0&&low[i]>G[pos][i])                low[i]=G[pos][i];        }//更新low数组    }    return result;}int main(){    while(cin>>n)    {        memset(G,inf,sizeof(G));//赋初始值inf        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)            {                cin>>x;                G[i][j]=G[j][i]=x;//最大生成树则是把权值改成负数,最终结果再加个负号            }        ans=prim();        cout<<ans<<endl;    }    return 0;}

2.Kruskal算法

首先相对Prim不同的是,这是一种针对边来解决问题的一种方法,因此,定义结构,保存边的两点,权重等信息。

另外,要求最小,必然对权重排序。

然后不断纳入新的联通分量的点。(并查集知识)

#include<iostream>#include<algorithm>using namespace std;struct Edge{    int u,v,w;} edge[105];//结构体包括边的两个连接点和权重int parent[105];int findpa(int x){    return parent[x]==x?x:findpa(parent[x]);}//查找是否在同一个连通分量中bool cmp(Edge a,Edge b){    return a.w<b.w;}//按权值排序void Init(){    for(int i=1; i<=105; i++)        parent[i]=i;//开始每个都是一个单独的联通分量}int main(){    int n;    cin>>n;    Init();    for(int i=0; i<n; i++)        cin>>edge[i].u>>edge[i].v>>edge[i].w;    sort(edge,edge+n,cmp);    int ans=0;    for(int i=0; i<n; i++)    {        int x=findpa(edge[i].u);        int y=findpa(edge[i].v);        if(x!=y)//判断两点是否在同一个连通分量中,是就不要,不是就纳入        {            parent[y]=x;            ans+=edge[i].w;        }    }    cout<<ans<<endl;    return 0;}

前导知识:并查集

算法描述:并查集用来解决判断两个节点是否相连通,且不需要给出的具体路径(需要给路径另外考虑dfs等算法)

算法核心:指定唯一根节点,判断两节点是否有共同的根节点。单纯向上寻找可能很费时间,且因为不用保留路径因此可以使用路径压缩:既令同一个连通分量中的元素直接指向父节点。

核心代码 :简单模版

void init()//一开始每个人都是独立一个连通分量{    cnt = 0;    for (int i=0; i<=m; i++) p[i] = i;}int find(int x)//不断查找父节点{    return p[x]==x ? x : p[x] = find(p[x]);}void merge(int a, int b){    int pa = find(a), pb = find(b);    p[pa] = pb; }


PS:关于最小生成树的基本上都是在模版上大致改动。准备一个好用的模版很重要~



0 0
原创粉丝点击