[Kruskal] UVA 1395

题意

给出一个n节点的图,求苗条度(最大边减最小值)尽量小的最小生成树

思路

边按权值排序,然后就要找一个区间【l,r】,可以构成最小生成树,然后最大边减去最小边的权值,这样一次枚举区间左侧,更新最小值。

代码

#include <algorithm>
#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;
typedef long long ll;

const int INF = 0x7f7f7f7f;
const int maxn = 100 + 10;
const int maxm = maxn * maxn;

struct Edge {
    int u, v;
    int dis;
    bool operator< ( const Edge &rhs ) const { return dis < rhs.dis; }
};

int n, m;
Edge edg[ maxm ];
int fa[ maxn ];
int findset ( int x ) { return fa[ x ] == -1 ? x : fa[ x ] = findset ( fa[ x ] ); }

void init ( int _n ) {
    n = _n;
    m = 0;
    memset ( fa, -1, sizeof ( fa ) );
}

void Addedge ( int u, int v, int w ) { edg[ m++ ] = Edge{u, v, w}; }

int kruskal ( int st, int ed ) {
    int sum = 0, cnt = 0;
    memset ( fa, -1, sizeof ( fa ) );

    //第一条最小的边一定会被选到
    for ( int i = st; i <= ed; ++i ) {
        int u = edg[ i ].u, v = edg[ i ].v;
        if ( findset ( u ) != findset ( v ) ) {
            sum = max ( sum, edg[ i ].dis ); //记录最大的边

            fa[ findset ( u ) ] = findset ( v );
            if ( ++cnt >= n - 1 )
                return sum - edg[ st ].dis;
        }
    }
    return INF;
}

int main () {
#ifdef LOCAL
    freopen ( "in", "r", stdin );
#endif
    int n, m;
    while ( ~scanf ( "%d%d", &n, &m ) && n ) {
        init ( n );
        for ( int i = 0; i < m; ++i ) {
            int u, v, w;
            scanf ( "%d%d%d", &u, &v, &w );
            Addedge ( u, v, w );
        }
        sort ( edg, edg + m );

        int ans = INF;
        //枚举i作为最小的边的时候, 所生成的最小生成树
        for ( int i = 0; i < m; ++i ) {
            int tmp = INF;
            for ( int j = i + n - 2; j < m; ++j ) {
                //剪枝,否则TLE
                if ( tmp != INF || edg[ j ].dis - edg[ i ].dis > ans )
                    break;
                tmp = kruskal ( i, j );
                ans = min ( ans, tmp );
            }
        }

        if ( ans == INF )
            printf ( "-1\n" );
        else
            printf ( "%d\n", ans );
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值