问题 N: 【基础图论】苗条的生成树
时间限制: 1 Sec 内存限制: 64 MB提交: 5 解决: 1
[ 提交][ 状态][ 讨论版]
题目描述
给定一个带权无向图G,你可以按如下方法定义一棵生成树: 图 G 是一个顶点与边的集合 (V, E), V 表示顶点集合 {v1, v2, …, vn} , E 表示无向的边的集合 {e1, e2, …, em}. 每条边 e ∈ E 有一个权值 w(e). 生成树 T 是一棵树(一个连通的无环子图) ,它用 n - 1 条边连接了全部的 n 个顶点。 生成树T的苗条度定义为生成树的n - 1条边中权值最大的边和权值最小的边的权值之差。 例如,图5(a)有4个顶点 {v1, v2, v3, v4} 和5条边 {e1, e2, e3, e4, e5}. 各条边的权值分别为 w(e1) = 3, w(e2) = 5, w(e3) = 6, w(e4) = 6, w(e5) = 7 ,如图5(b)所示.
该图存在多棵生成树,图6(a)~(d)展示了其中的4棵。生成树 Ta 的3条边的权值分别是 3, 6 和 7. 权值最大为 7 最小为 3,因此 Ta 的苗条度为 4。 相应的,生成树 Tb, Tc 和 Td 的苗条度分别是3 , 2 和 1。 显而易见,任意一棵生成树的苗条度都大于或等于1,因此Td是所有生成树中最苗条的。 你的任务就是写一个程序来计算出最小的苗条度。
输入
输入文件包含多组测试数据,每组测试数据格式如下: 第1行:2个空格分开的整数n m (2 ≤ n ≤ 100 and 0 ≤ m ≤ n(n − 1)/2),n表示顶点数,m表示边数。 接下来m行,每行3个空格分开的整数a b w(1 ≤ w ≤ 10000) , 表示顶点a与顶点b之间有一条边,权值为w。输入数据保证图G是一条简单图,也就是说,不存在自环,两条顶点之间仅有一条边。 最后一组测试数据后的2个空格分开的0表示输入结束。
备注:同样题目数据加强版:OJ2586.
输出
对每组测试数据,如果图存在生成树,输出生成树的苗条度最小的值;否则,输出-1。
样例输入
4 5
1 2 3
1 3 5
1 4 6
2 4 6
3 4 7
4 6
1 2 10
1 3 100
1 4 90
2 3 20
2 4 80
3 4 40
2 1
1 2 1
3 0
3 1
1 2 1
3 3
1 2 2
2 3 5
1 3 6
5 10
1 2 110
1 3 120
1 4 130
1 5 120
2 3 110
2 4 120
2 5 130
3 4 120
3 5 110
4 5 120
5 10
1 2 9384
1 3 887
1 4 2778
1 5 6916
2 3 7794
2 4 8336
2 5 5387
3 4 493
3 5 6650
4 5 1422
5 8
1 2 1
2 3 100
3 4 100
4 5 100
1 5 50
2 5 50
3 5 50
4 1 150
0 0
样例输出
1
20
0
-1
-1
1
0
1686
50
分析:
要解这道题必须要了解kruskal算法,如何生成不同的最小生成树?可以发现,在将边从小到大排序后,必然有一条边是最大的且这条边是生成树中必需的一条边,意思就是,排序后,最小生成树的苗条度必定不超过最大边减去最小边。
在生成树过后,1~m条边中会有一条生成树中最大的边,因为最小生成树的性质,如果我们把边从后往前删,对结果是没有影响的,所以需要将每次生成树开始查找的起始边从小到大循环,这样,生成树的最小边就可能会改变,就能生成不同的树
还有,当总边数小于n-1时,就不可能有树,这是循环的终止条件
代码如下:
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,s;
struct node
{
int l,r,o;
bool operator < (const node &p)const {return o<p.o;}
}a[5100];//空间记得开够
int f[110];
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
void kus(int k)
{
int x,y,p,h=0;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=k;i<=m&&h<n-1;i++)
{
x=find(a[i].l);y=find(a[i].r);
if(x!=y) f[y]=x,p=a[i].o-a[k].o,h++;
}
if(h>=n-1) s=min(s,p);
}
int main()
{
freopen("slight-tree.in","r",stdin);
freopen("slight-tree.out","w",stdout);
while(scanf("%d%d",&n,&m),n||m)//n,m可以有一个为0
{
s=10000000;
//if(m<n-1) goto hehe;不能写在这,不然输入不完
for(int i=1;i<=m;i++)
scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].o);
if(m<n-1) goto hehe;
sort(a+1,a+1+m);
for(int i=1;m-i+1>=n-1;i++)//循环最小边
kus(i);
hehe:
if(s==10000000) s=-1;
printf("%d\n",s);
}
}