下文摘自刘汝佳算法紫书
题意:
给出一个n(n≤100)节点的图,求苗条度(最大边减最小边)尽量小的生成树。
解题思路:
首先把边按权值从小到大排序。对于一个连续的边集区间[L,R],如果这些边使得n个点全部连通,则一定存在一个苗条度不超过W[R]-W[L]的生成树(其中W[I]表示排序后第i条边的权值)。
从小到大枚举L,对于每个L,从小到大枚举R,同时用并查集将新进入[L,R]的边两端的点合并成一个集合,与kruskal算法一样。当所有的点连通时停止枚举R,换下一个L(并把R重置为L)继续枚举。
代码如下
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 10005
#define inf 0x3f3f3f3f
int n,m;
int p[maxn],v[maxn],u[maxn],w[maxn],r[maxn];
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 kruskal(int cur)
{
for(int i=1; i<=n; i++)
p[i]=i;
int ans=-1;
for(int i=cur; i<m; i++)
{
int e=r[i];
int x=find(u[e]);
int y=find(v[e]);
if(x!=y)
{
ans=max(ans,w[e]-w[r[cur]]);
p[x]=y;
}
}
int flag=find(1);
for(int i=1; i<=n; i++)
if(flag!=find(i)) return inf;
return ans;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d%d",&n,&m)&&n+m)
{
for(int i=0; i<m; i++)
scanf("%d%d%d",&u[i],&v[i],&w[i]);
if(m<n-1)
printf("-1\n");
else
{
for(int i=0; i<m; i++)
r[i]=i;
sort(r,r+m,cmp);
int ans=inf;
for(int i=0; i<m; i++)
ans=min(ans,kruskal(i));
if(ans!=inf) printf("%d\n",ans);
else printf("-1\n");
}
}
return 0;
}