UVA1395-Slim Span(最小生成树Kruskal、并查集)
题目链接:点击打开链接
题意:给你一个图,求一个生成树,使其苗条度最小
苗条度:生成树的最大边减去最小边的值。
先把边的权值从大到小排序,然后,对于任意一个边值区间,都可以建立生成树,使苗条度不超过这个图的最大边减最小边。
所以说,要想找出来这个苗条度最小的生成树,就要先枚举这个区间的左端点,在此基础去枚举右端点,比如说有三个权值是3,4,5的边,首先是[3],[3,4][3,4,5]分别去建树,然后比较苗条度,取最小的,这样,所有的方案都建成之后,最小的苗条度也就出来了,当然,边要足够连接所有的点,建不了树是没有结果的,返回-1
代码及注释如下:
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,cnt;
int a[11000],b[11000],w[11000],p[11000],r[11000];//a,b为点的序号。w为权值,p为并查集,r是边的序号
int cmp(const int i,const int j)
{
return w[i]<w[j]; //间接排序
}
int find(int x)
{
return p[x]==x?x:p[x]=find(p[x]); //找根函数
}
int solve()
{
int ans=0x3f3f3f3f,t;
for(int i=0;i<m;i++)r[i]=i; //边序号初始化
sort(r,r+m,cmp); //将边的权值从小到大排序
for(int i=0;i<m;i++)
{
for(int i=1;i<=n;i++)p[i]=i; //点的序号是从1开始,且并查集是对每个序号的点初始化,所以初始化时要从1-n !
cnt=n-1; //这个cnt是用来判断是否所有的点都被合并
for(int j=i;j<m;j++)
{
int R=r[j];
//以下三行为并查集操作
int x=find(a[R]);int y=find(b[R]);
if(x!=y)
{
p[x]=y;
cnt--;
}
//合并成树以后,才能判断是否有最小的苗条度! 所以成不了树就不能比较最小值了,因为没有苗条度
if(!cnt)
{
ans=min(ans,w[R]-w[r[i]]);
break;
}
}
}
return ans==0x3f3f3f3f?-1:ans;
}
int main()
{
//freopen("input.txt", "r", stdin );
//freopen("output.txt", "w", stdout);
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0)break;
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&a[i],&b[i],&w[i]);
}
printf("%d\n",solve());
}
return 0;
}