poj 3522 && uva 1395 && la 3887

来源:互联网 发布:算法流程图怎么画 编辑:程序博客网 时间:2024/06/06 04:30

题目概述

有N个点,M条边,问是否可构成一生成树,若可,求最长边与最短边差值的最小值
没有自环或平行边

时限

5000ms/15000ms

输入

第一行整数N,M,其后M行,每行三个整数a,b,c,描述a,b之间有一条边,权值为c,输入到N=M=0结束

限制

2<=N<=100

输出

每行一个数,若无生成树,为-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算法,按原题说是最瘦生成树,首先有一点要明白,按权值排序之后,数组中相邻的两条边权值差最小,那么要想使得差最小就要从数组中连续取边构成树,这样才能保证第一条边和最后一条边离的比较近,也就是差比较小,但同时也不难发现,这样会产生多个树,差也不同,因此需要枚举树的第一条边,从这条边开始取边,最后得到最小的差值
实现层面上,由于已经排序过,不需要优先队了,这样kruskal算法主体复杂度下降到与边数相关的线性级,由于是枚举的第一条边,最终复杂度是与边相关的平方级,边数也不是很多,总之是足够了

题解状态

224K,63MS,C++,1239B

题解代码

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define INF 0x3f3f3f3f#define MAXN 103#define memset0(a) memset(a,0,sizeof(a))struct Edge//边的结构{    int from, to, w;//起点 终点 权值    bool operator<(const Edge &b)const    {        return w < b.w;    }}graph[4953];//完全图时 100*99/2=4950int N, M;//点总数 边总数int least, most, sub, id[MAXN];//最小权 最大权 差值 并查集的父节点数组int UFfind(int a)//并查集的查根 这可比递归快多了{    while (a != id[a])        a = id[a];    return a;}bool kruskal(int p)//kruskal算法主体 参数是开始的边的下标{    int cnt = N;    for (int p = 1; p <= N; p++)        id[p] = p;    int a, b;    for (int i = p; i < M&&cnt != 1; i++)        if ((a = UFfind(graph[i].from)) != (b = UFfind(graph[i].to))) {            cnt--;            id[a] = b;            least = min(least, graph[i].w);            most = max(most, graph[i].w);            if (most - least > sub)//当差值已经大于之前的最优情况时就没比要再算了                return 1;//直接视为构成了生成树返回即可        }    return cnt == 1;//如果并查集有多个 则是不成树}int fun(){    for (int p = 0; p < M; p++)        scanf("%d%d%d", &graph[p].from, &graph[p].to, &graph[p].w);//input    sort(graph, graph + M);//一次排序足矣    sub = INF;//如果不初始化为INF 中途会因为剪枝而提前返回错误结果    most = least = graph[0].w;    if (!kruskal(0))//第一次运行检查是否可成树        return -1;    sub = most - least;//顺手初始化差值    for (int p = 1; p < M; p++) {        least = most = graph[p].w;        if (kruskal(p))//只有成树了才取最小            sub = min(sub, most - least);    }    return sub;}int main(void){    //freopen("vs_cin.txt", "r", stdin);    //freopen("vs_cout.txt", "w", stdout);    while (~scanf("%d%d", &N, &M) && (N || M))//input        printf("%d\n", fun());//output}

EOF

0 0