题意
给出一个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;
}