例题11-2 苗条的生成树(Slim Span, ACM/ICPC Japan 2007, UVa1395)

欢迎访问我的Uva题解目录 https://blog.****.net/ri*qi/article/details/81149109

题目描述

例题11-2 苗条的生成树(Slim Span, ACM/ICPC Japan 2007, UVa1395)

题意解析

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

算法设计

参照《算法竞赛入门经典(第2版)》中的提示:

首先把边按权值从小到大排序。对于一个连续的边集区间[L, R],如果这些边使得n个点全部连通,则一定存在一个苗条度不超过W[R]-W[L]的生成树(其中W[i]表示排序后第i条边的权值)。
从小到大枚举L,对于每个L,从小到大枚举R,同时用并查集将新进入[L,R]的边两端的点合并成一个集合,与Kruskal算法一样。当所有点连通时停止枚举R,换下一个L(并且把R重置为L)继续枚举。

C++代码

#include<bits/stdc++.h>
using namespace std;
struct Edge{//边的类
    int v1,v2,weight;
    Edge(int vv1,int vv2,int w):v1(vv1),v2(vv2),weight(w) {}
};
int n,m,father[105];//n、m、并查集
vector<Edge>edges;//存储所有边
int findFather(int x){//查找父结点并进行路径压缩
    if(x==father[x])
        return x;
    int t=findFather(father[x]);
    father[x]=t;
    return t;
}
void unionSets(int a,int b){//合并两个集合
    int ua=findFather(a),ub=findFather(b);
    if(ua!=ub)
        father[ua]=ub;
}
bool countRoots(){//确认并查集内是否只有一个集合
    int num=0;
    for(int i=1;i<=n;++i)
        if(father[i]==i)
            ++num;
    return num==1;
}
int main(){
    while(~scanf("%d%d",&n,&m)&&n!=0){
        edges.clear();
        while(m--){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            edges.push_back(Edge(a,b,c));
        }
        sort(edges.begin(),edges.end(),[](const Edge&e1,const Edge&e2){//排序
            return e1.weight<e2.weight;
        });
        int ans=INT_MAX;//最终结果
        for(int i=0;i<edges.size();++i){//从小到大枚举i
            iota(father,father+n+1,0);//初始化并查集
            for(int j=i;j<edges.size()&&j<i+n-2;++j)//将i~i+n-3这n-2条边两端的点合并成一个集合
                unionSets(edges[j].v1,edges[j].v2);
            for(int j=i+n-2;j<edges.size();++j){//从小到大枚举j
                unionSets(edges[j].v1,edges[j].v2);//将第j条边两端的点合并成一个集合
                if(countRoots()){//并查集内只有一个集合,表示所有点已连通
                    ans=min(ans,edges[j].weight-edges[i].weight);//更新ans
                    break;
                }
            }
        }
        if(ans==INT_MAX)
            puts("-1");
        else
            printf("%d\n",ans);
    }
    return 0;
}