最小生成树总结(Kruskal算法+Prim算法)

最小生成树(或称为最小代价生成树)

对无向连通图的生成树,各边的权值总和称为生成树的权,权最小的生成树称为最小生成树。

构成最小生成树的三条准则:

1.必须只使用该网络中的边来构造最小生成树

2.必须使用且仅使用n-1条边来联结网络中的n个顶点

3.不能使用产生回路的边

常用的构造最小生成树算法:克鲁斯卡尔(Kruskal)算法,普里姆(Prim)算法

MST算法对比分析

算法最坏情况下时间复杂度特点
Prim算法O(n^2)适合于稠密图
Kruskal算法O(elge)排序时间开销占主导地位

克鲁斯卡尔算法

算法思想:

以边为主导地位,始终都是选择当前可用的最小权值的边。

实现步骤:

从网络中选择权值最小的边,将所有边的信息存放到一个结构体中,并将结构体数组按照权值从大到小排序,依次选用该网络的边。

选择权值最小的边后,要判断两个顶点是否属于同一个连通分量,如果是,则要舍去,不是,则选用,并将这两个连通分量合并为一个连通分量。(并查集实现)

Constructing Roads
http://poj.org/problem?id=2421

以上题为例

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[10010][10010];
struct node
{
    int u,v,w;
} e[100010];//结构体存每个点与边之间的权值
int par[100010];
int n,q,d,i,j,t,sum;
bool cmp(node x,node y)
{
    return x.w<y.w;//排序以便后续每次找最小的权值
}
void init()
{
    for(int i=1; i<=n; i++)
        par[i]=i;//初始化
}
int getf(int v)//找根节点
{
    if(par[v]==v)
        return v;
    else
        return par[v]=getf(par[v]);
}
void merge(int u,int v,int w)//合并两个连通分量为一个连通分量
{
    int t1,t2;
    t1=getf(u);
    t2=getf(v);
    if(t1!=t2)
    {
        par[t2]=t1;
        t++;//已连的边的个数
        sum+=w;//加权
    }
}
int main()
{
    scanf("%d",&n);
    init();
    int k=0;
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            scanf("%d",&d);
            if(i<j)
            {
                e[k].u=i;
                e[k].v=j;
                e[k++].w=d;
            }
        }
    }
    scanf("%d",&q);
    for(int i=1; i<=q; i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        merge(x,y,0);
    }
    t=0;
    sum=0;
    sort(e,e+k,cmp);
    for(int i=0; i<k; i++)
    {
        if(t==n-1)//当连的边等于n-1,说明所有点已连接
            break;
        merge(e[i].u,e[i].v,e[i].w);
    }
    printf("%d\n",sum);
    return 0;
}

普里姆算法

算法思想:

以顶点为主导地位,从起始顶点出发,通过选择当前可用的最小权值边依次把其他顶点加入到生成树当中来。

具体过程:

1.从无向连通图中选择一个起始顶点u0,首先将它加入到集合T中,然后选择与u0关联的,具有最小权值的边(u0,v),将顶点v加入到顶点集合T中。

  1. 每一次都找与上一个点有关联且具有最小权值的边加入,如此持续下去,直到网络中的所有顶点都加入到生成树顶点集合T中为止。

Building a Space Station
http://poj.org/problem?id=2031

以这个题为例:

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
double dis[110];
double x[110],y[110],z[110],r[110];
double mp[110][110];
int book[110];
const double inf=0x3f3f3f3f3f;
int main()
{
    int n;
    while(~scanf("%d",&n)&&n)
    {
        if(n==0)
            break;
        memset(mp,0,sizeof(mp));
        //double x,y,z,r;
        double R;
        for(int i=1; i<=n; i++)
            scanf("%lf %lf %lf %lf",&x[i],&y[i],&z[i],&r[i]);
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                double t=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])+(z[i]-z[j])*(z[i]-z[j]));
                R=r[i]+r[j];
                if(t>R)
                    mp[i][j]=mp[j][i]=t-R;
                else
                    mp[i][j]=mp[j][i]=0;
            }
        }
        memset(book,0,sizeof(book));
        for(int i=1; i<=n; i++)
            dis[i]=mp[1][i];//初始化
         book[1]=1;
        double ans=0.0,minn;
        int u;
        //prim算法模板
        for(int i=1;i<n;i++)
        {
            minn=inf;
            for(int j=1;j<=n;j++)
            {
                if(!book[j]&&dis[j]<minn)
                {
                    minn=dis[j];
                    u=j;//选取一个顶点
                }
            }
            book[u]=1;
            ans+=dis[u];
            for(int v=1;v<=n;v++)
            {
                if(!book[v]&&dis[v]>mp[u][v])//每次找与上一个顶点相关的且权值最小的
                {
                    dis[v]=mp[u][v];
                }
            }
        }
        printf("%.3f\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值