uva 1395Slim Span(最小生成树变形)

题意:给出有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;
}

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/80241572