UVA1395 苗条的生成树 Slim Span

传送门

求一棵生成树,使它的最大边权与最小边权之差最小。

假设已经求出了一棵最小生成树,它的最小边权一定是图上所有边中最小的;它的最大边也应该是相对最小的。

也就是说,不改变当前最小边,它的边权差只能变大,答案不可能变得更优了。

那么想要答案更新,只可能是最小边变大,最大边也变大。

那么删去最小边,加入比当前树上最大的边稍大的边,如果图依旧联通,就可以检查是否更新答案。

但是这样...想要检查图是否联通,复杂度是O(n*m)的。这样总复杂度就是O(n*m^2)的,显然不行。

思考一下,想删除最小边,不如把这条边标记为不可用后,重新跑一边最小生成树。

最小生成树的复杂度是O(nlogn),每条边删除一次就是O(m*nlogn)。

其实整理一下,就是枚举每一条边作为最小边,更新答案就可以了。

代码如下

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
#include<algorithm>
using namespace std;
const int maxn = 1e6;
const int INF = 0X3f3f3f3f;
int n,m;
int ans,fa[maxn];
struct edge {
    int u,v,val;
    bool operator < (const edge & N)const {
        return val < N.val;
    }
} e[maxn];

int getfa(int x) {
    if(fa[x] == x) return x;
    return fa[x] = getfa(fa[x]);
}

void init() {
    for(int i = 1; i <= n; i++)
        fa[i] = i;
}

void kruskal(int s) {
    init();
    int mx,mn;
    int cnt = 0;
    for(int i = s; i <= m; i++) {
        int x = getfa(e[i].u);
        int y = getfa(e[i].v);
        if(x == y)continue;
        fa[x] = y;
        cnt++;
        if(cnt == 1)mn = e[i].val;
        if(cnt == n-1)mx = e[i].val;
    }
    if(cnt < n-1)return;
    ans = min(ans,mx-mn);
}


int main() {
    while(scanf("%d%d",&n,&m)) {
        if(!n && !m)break;
        ans = INF;
        for(int i = 1; i <= m; i++)
            scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].val);
        sort(e+1,e+m+1);
        for(int i = 1; i <= m; i++)
            kruskal(i);
        if(ans == INF) printf("-1
");
        else printf("%d
",ans);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/mogeko/p/10939803.html