UVA1395 苗条的生成树 Slim Span —— kruskal算法

kruskal算法

           求加权连通图的最小生成树的算法。

          

            Kruskal算法的第一步是给所有边按照从大到小的顺序排列。这一步可以直接使用库函数qsort或者sort.接下来从小到大一次考察每个边(u,v)。
情况1:u和v在同一个连通分量中,那么加入(u,v)后会形成环,因此不能选择。
情况2:如果u和v在不同的连通分量,那么加入(u,v)一定是最优的。

        与并查集的结合使问题得以解决

UVA1395 苗条的生成树 Slim Span

题意翻译

题意简述

求所有生成树中最大边权与最小边权差最小的,输出它们的差值。

输入:

输入文件包含多组测试数据,每组测试数据如下: 第1行:2个整数n m (2 ≤ n ≤ 100 and 0 ≤ m ≤ n(n − 1)/2),n表示顶点数,m表示边数。接下来m行,每行3个空格分开的整数a b w(1 ≤ w ≤ 10000) , 表示顶点a与顶点b之间有一条边,权值为w。

输出:

对每组测试数据,如果图存在生成树,输出生成树的差值最小的;否则,输出-1。

样例输入:

4 5
1 2 3
1 3 5
1 4 6
2 4 6
3 4 7
4 6
1 2 10
1 3 100
1 4 90
2 3 20
2 4 80
3 4 40
2 1
1 2 1
3 0
3 1
1 2 1
3 3
1 2 2
2 3 5
1 3 6
5 10
1 2 110
1 3 120
1 4 130
1 5 120
2 3 110
2 4 120
2 5 130
3 4 120
3 5 110
4 5 120
5 10
1 2 9384
1 3 887
1 4 2778
1 5 6916
2 3 7794
2 4 8336
2 5 5387
3 4 493
3 5 6650
4 5 1422
5 8
1 2 1
2 3 100
3 4 100
4 5 100
1 5 50
2 5 50
3 5 50
4 1 150
0 0

样例输出


1
20
0
-1
-1
1
0
1686
50

  思路:

  第一步:建一个结构体把边的两个端点和边的权值存入结构体;

  第二步:根据每条边权值大小进行sort从小到大排序

  第三步:遍历每一条边,对每一个符合条件的边(不会成环)记录下每一次的差值并储存到ans变量中

 

    Find函数和Merge函数是并查集的基本用法,不同的是在Merge函数里  对判断不在同一集合的两个点的返回值为1 

    kruskal函数中先对pri[i]数组赋初值(pri[i]数组存储的是 i  点的根节点是谁),对k之后的每一个点进行判断并更新最大边的权值存入xmax中(因为开始已经对边的权值从小到大排过序了)

    直到找到最后一个点(如果没有到最后一个点,返回值是0,ans值不会改变,就输出-1,否则一直更新ans为最小差值,最后输出)

 

完整代码

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<math.h>
typedef long long ll;
const int xmax2=1e5+7;
const int INF=1e9+7;
using namespace std;
int n,m,xmax,ans,cnt;

struct node{
int u,v,w;    ///两个端点和边权值
}edge[xmax2];

int pre[xmax2];  ///存储根节点

int Find(int x)   ///查找函数
{
    if(pre[x]==x)
        return x;
    else
        return pre[x]=Find(pre[x]);    ///路径压缩
}

int Merge(int x,int y)
{
    int fx=Find(x);
    int fy=Find(y);
    if(fx!=fy)
    {
        pre[fx]=fy;
        return 1;    ///不在一个集合里返回1
    }
    return 0;
}

int cmp(node x,node y)
{
    return x.w<y.w;      ///结构体排序,从小到大
}

int kruskal(int k)
{
    xmax=-1;
    cnt=0;    ///记录

    for(int j=1;j<=n;j++)
        pre[j]=j;   ///赋初值

    for(int i=k;i<=m;i++)
    {
        if(Merge(edge[i].u,edge[i].v)){
            ///如果满足不在一个集合里,更新xmax
            xmax=max(xmax,edge[i].w);   
            cnt++;
         if(cnt==n-1)
            return 1;    ///到了最后一个点返回1
        }
    }
            return 0;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF&&(n||m))
    {
        for(int i=1;i<=m;i++)
            scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);

            sort(edge+1,edge+m+1,cmp);

            ans=INF;    ///对ans赋初值为一个极大值

        for(int i=1;i<=m;i++)
        {
            if(kruskal(i))     ///满足条件就更新ans的值,否则ans值不变
                ans=min(ans,xmax-edge[i].w);
        }

        if(ans==INF)    ans=-1;
            printf("%d\n",ans);

    }
    return 0;
}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值