题目链接:
https://odzkskevi.qnssl.com/8e16f8701018d0e3529ac3ca319e1f67
中文题意:给出一个n(n<=100)结点的图,求苗条度(最大边减最小边的值)尽量小的生成树。
【分析】首先把边按权值从小到大排序。对于一个连续的边集区间[L,R],如果这些边使得n个点全部连通,则一定存在一个苗条度不超过W[R]-W[L]的生成树(其中W[i]表示排序后第i条边的权值)。
从小到大枚举L,对于每个L,从小到大枚举R,同时用并查集将新进入[L,R]的边的两端的点合并成一个集合,与Kruskal算法一样。当所有点连通时停止枚举R,换下一个L(并且把R重置为L)继续枚举。
下面是AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int pre[105],n,m;
struct node
{
int u,v,cost;
}a[50000];
void init()
{
for(int i=1;i<=n;i++)
{
pre[i]=i;
}
}
int fin(int x)
{
if(x==pre[x])
{
return x;
}
else
{
return pre[x]=fin(pre[x]);
}
}
void join(int x,int y)
{
int t1=fin(x);
int t2=fin(y);
if(t1!=t2)
{
pre[t1]=t2;
}
}
bool cmp(node x,node y)
{
return x.cost<y.cost;
}
int main()
{
while(scanf("%d%d",&n,&m)&&n)
{
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].cost);
}
sort(a,a+m,cmp);
int re=10000000;
for(int i=0;i<m;i++)
{
init();
int sum=0;
for(int j=i;j<m;j++)
{
if(fin(a[j].u)!=fin(a[j].v))
{
join(a[j].u,a[j].v);
sum++;
}
if(sum==n-1)
{
re=min(re,a[j].cost-a[i].cost);
break;
}
}
}
if(re!=10000000)
printf("%d\n",re);
else
{
printf("-1\n");
}
}
return 0;
}