训练第五周之最小生成树

本篇纯属鬼扯,因为不是很想看大神们写的Prim算法和Kruskal算法,所以靠自己理解写一下这两天刷的最小生成树的题目,希望以后能把本篇修正。

Kruskal算法

一、定义:

最小生成树是指通过算法找到搜索到的边子集所构成的树中,不但包括了加权连通图里的所有顶点 (英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。

二、概述:

设G=(V,E)是一个加权连通图,首先定义一个结构体数组struct line r[maxm]存放节点之间的权值,然后将结构体数组按权值从小到大排序,依次取出r[i]比较,如果r[i]中节点已经加到树中,则看下一个,如果还未加入,则将未加入的点放入树中,不必按大小顺序,直至树中边集达到n-1为止,n为节点总数。这样求出的树一定是最小生成树,这也是贪心思想的一种体现。

三、步骤:

1.定义结构体struct line {int a,b,c}r[maxm];并按题意赋值
2.将结构体数组按权值从小到大排序
3.定义pre[maxn]数组,用来记录父节点,并初始化pre[i]=i
4.定义数组Find(int x),用来找到与x节点连通的最前面的父节点,就好像找到最大的boss,不一定直接相连,只要连通的就行
4.建立循环,i从1…m,如果Find(r[i].a)!=Find(r[i].b),就令pre[Find(r[i].a)]=Find(r[i].b),即令Find(r[i].b)做为Find(r[i].a)的父节点。也可以用tot < n-1作为控制循环结束的条件,tot 为边数
5.循环结束形成的树为最小生成树,只是按此算法每一层次并无明显大小关系

五、附图:

这里写图片描述

六、核心代码

自己代码:

#define INF 99999999
int pre[maxn];
struct line
{
    int a,b,c;//a,b为节点,c为权值
}r[maxm];
int Find(int x)//找到最大父节点
{
    return pre[x]==x?x:Find(pre[x]);
}
bool cmp(struct line x,struct line y)
{
    return x.c<y.c;
}
void Kruskal()
{
    int i;
    for(i=1;i<=n;i++)
        pre[i]=i;//初始化父节点数组
    sort(r+1,r+m+1,cmp);//将结构体数组按权值从小到大排序
    //int maxline=0;
    for(i=1;i<=m;i++)
    {
        if(Find(r[i].a)!=Find(r[i].b))
        {
            pre[Find(r[i].a)]=Find(r[i].b);
            /*if(maxline<r[i].c)//用来求最小生成树的最大边
                maxline=r[i].c;*/
        }
    }
}

大神代码:

using namespace std;
#define INF 99999999
int pre[maxn];
struct line
{
    int a,b,c;
}r[maxm];
int Find(int x)
{
    return pre[x]==x?x:Find(pre[x]);
}
bool cmp(struct line x,struct line y)
{
    return x.c<y.c;
}
void kruskal()
{
    int i;
    for(i=1;i<=n;i++)
        pre[i]=i;
    sort(r+1,r+m+1,cmp);
    //int maxline=0;
    int tot=0;
    i=1;
    while(tot<n-1)
    {
        while(i<=m&&Find(r[i].a)==Find(r[i].b)) i++;
        if(i>m) break;
        pre[Find(r[i].a)]=Find(r[i].b);
        /*if(maxline<r[i].c)
            maxline=r[i].c;*/
        tot++;
    }
    //printf("%d\n",maxline);
    return 0;
}

七、缺点:

图的存贮结构采用边集数组,且权值相等的边在数组中排列次序可以是任意的.该方法对于边相对比较多的不是很实用,浪费时间.

八、例题

1、Out of Hay-poj2395

题目概述:求出Bessie所要带的最多的水,因为她每到一个农场都可以补充水,所以就是求最小生成树的最大边
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 99999999
int pre[2005];
int Find(int x)
{
    return pre[x]==x?x:Find(pre[x]);
}
struct line
{
    int a,b,c;
}r[10005];//这里大小应该按maxm,而我一直按的maxn结果RE了4次
bool cmp(struct line x,struct line y)
{
    return x.c<y.c;
}
int main()
{
    int n,m;
    int i;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        pre[i]=i;
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&r[i].a,&r[i].b,&r[i].c);
    }
    sort(r+1,r+m+1,cmp);
    int maxline=0;
    int tot=0;
    i=1;
    while(tot<n-1)
    {
        while(i<=m&&Find(r[i].a)==Find(r[i].b)) i++;
        if(i>m) break;
        pre[Find(r[i].a)]=Find(r[i].b);
        if(maxline<r[i].c)
            maxline=r[i].c;
        tot++;
    }
    printf("%d\n",maxline);
    return 0;
}

2、还是畅通工程-hdu1233

题目概述:全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小,很简单的一个最小生成树题目
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int pre[5000];
struct line
{
    int a,b,c;
}r[5000];
bool cmp(struct line x,struct line y)
{
    return x.c<y.c;
}
int Find(int a)
{
    return pre[a]==a?a:Find(pre[a]);
}
int main()
{
    int n,m,i,j,l;
    while(scanf("%d",&n)!=EOF&&n)
    {
        for(i=1;i<=n;i++)
            pre[i]=i;
        m=n*(n-1)/2;
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&r[i].a,&r[i].b,&r[i].c);
        }
        sort(r+1,r+m+1,cmp);
        l=0;
        for(i=1;i<=m;i++)
        {
            if(Find(r[i].a)==Find(r[i].b))
                continue;
            else if(Find(r[i].a)>Find(r[i].b))//当时比较傻,非要给节点分出个大小关系,结果搞得不伦不类了
            {
                l+=r[i].c;
                //printf("%d %d %d\n",Find(r[i].a),Find(r[i].b),road[Find(r[i].a)][Find(r[i].b)]);
                pre[Find(r[i].a)]=Find(r[i].b);
            }
            else
            {
                l+=r[i].c;
                //printf("%d %d %d\n",Find(r[i].a),Find(r[i].b),road[Find(r[i].a)][Find(r[i].b)]);
                pre[Find(r[i].b)]=Find(r[i].a);
            }
        }
        printf("%d\n", l);
    }
    return 0;
}

3、Network-poj1861

题目概述:求让每个中心枢纽相通的最小生成树,输出最小生成树最大边,以及最小生成树的边数和边集
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 99999999
#define maxm 15005
#define maxn 1005
struct line
{
    int a,b,c;
}r[maxm];
int pre[maxn];//记录父节点
bool s[maxm]={0};//记录使用的边
int n,m;
int Find(int x)//寻找最大父节点
{
    return pre[x]==x?x:Find(pre[x]);
}
bool cmp(struct line x,struct line y)//按权值从小到大排序
{
    return x.c<y.c;
}
int main()
{
    int i;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&r[i].a,&r[i].b,&r[i].c);
    }
    for(i=1;i<=n;i++)
    {
        pre[i]=i;//初始化父节点为自己
    }
    sort(r+1,r+m+1,cmp);
    int maxline=0;//记录最大边
    int ans=0;//记录使用的总边数
    for(i=1;i<=m;i++)
    {
        if(Find(r[i].a)!=Find(r[i].b))
        {
            pre[Find(r[i].a)]=Find(r[i].b);
            ans++;
            s[i]=1;
            if(maxline<r[i].c)
                maxline=r[i].c;
        }
    }
    printf("%d\n%d\n",maxline,ans);
    for(i=1;i<=m;i++)
    {
        if(s[i])//s[i]==1证明使用了该边,输出对应节点
            printf("%d %d\n",r[i].a,r[i].b);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值