题意:给出有n个节点的图,求最大边的值-最小边的值尽量小的生成树。
思路:就和紫书上讲的一样。先对边进行预处理排序。然后枚举左区间L,然后对于每个L,枚举R(右区间),如果是一棵树,那么这个区间上的最小值就是W[r]-W[l],(因为是按w的大小排过序的)求出在[L,R]区间上的最小值。
一开始以为是对于每一个枚举的L,R的枚举是要枚举到最后的m(边的长度),然后就超时了。后来仔细一想,其实并不需要,只要枚举到刚好是一颗树的时候就可以停止了,可以枚举下一个L了。这个是可以用反证法证明的。
#include <bits/stdc++.h>
using namespace std;
int n, m;
const int inf = 0x3f3f3f3f;
const int maxn = 105;
struct node {
int u, v, w;
} e[maxn * maxn / 2];
bool cmp(node a, node b) {
return a.w < b.w;
}
int f[maxn];
int find(int v) {
return f[v] == v ? v : f[v] = find(f[v]);
}
int kruskal(int i, int j) {
for(int k = 1; k <= n; k++)f[k] = k;
int ans = 0;
for(int k = i; k <= j; k++) {
int t1 = find(e[k].u);
int t2 = find(e[k].v);
if(t1 != t2) {
ans++;
f[t1] = t2;
}
}
if(ans == n - 1) return e[j].w - e[i].w;
return inf;
}
int main() {
while(~scanf("%d%d", &n, &m), n + m) {
memset(e, 0, sizeof(e));
for(int i = 1; i <= m; i++) {
scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
}
int MIN = inf;
sort(e + 1, e + 1 + m, cmp);
for(int i = 1; i + n - 2 <= m; i++) { //上界
for(int j = i + n - 2; j <= m; j++) { //下界
int ret = kruskal(i, j);
MIN = min(MIN, ret);
if(ret != inf) break;
}
}
if(MIN == inf) printf("-1\n");
else printf("%d\n", MIN);
}
return 0;
}