poj 3763 Slim Span(变式Kruskal)

来源:互联网 发布:dijkstra算法 填空 编辑:程序博客网 时间:2024/05/16 14:41

poj 3763 Slim Span(变式Kruskal)
Time Limit: 5000ms Memory Limit: 65536kB

Description
Given an undirected weighted graph G, you should find one of spanning trees specified as follows.

The graph G is an ordered pair (V, E), where V is a set of vertices {v1, v2, …, vn} and E is a set of undirected edges {e1, e2, …, em}. Each edge e ∈ E has its weight w(e).

A spanning tree T is a tree (a connected subgraph without cycles) which connects all the n vertices with n − 1 edges. The slimness of a spanning tree T is defined as the difference between the largest weight and the smallest weight among the n − 1 edges of T.
这里写图片描述

Figure 5: A graph G and the weights of the edges
For example, a graph G in Figure 5(a) has four vertices {v1, v2, v3, v4} and five undirected edges {e1, e2, e3, e4, e5}. The weights of the edges are w(e1) = 3, w(e2) = 5, w(e3) = 6, w(e4) = 6, w(e5) = 7 as shown in Figure 5(b).

这里写图片描述
Figure 6: Examples of the spanning trees of G
There are several spanning trees for G. Four of them are depicted in Figure 6(a)~(d). The spanning tree Ta in Figure 6(a) has three edges whose weights are 3, 6 and 7. The largest weight is 7 and the smallest weight is 3 so that the slimness of the tree Ta is 4. The slimnesses of spanning trees Tb, Tc and Td shown in Figure 6(b), (c) and (d) are 3, 2 and 1, respectively. You can easily see the slimness of any other spanning tree is greater than or equal to 1, thus the spanning tree Td in Figure 6(d) is one of the slimmest spanning trees whose slimness is 1.

Your job is to write a program that computes the smallest slimness.

Input
The input consists of multiple datasets, followed by a line containing two zeros separated by a space. Each dataset has the following format.

n m
a1 b1 w1

am bm wm
Every input item in a dataset is a non-negative integer. Items in a line are separated by a space. n is the number of the vertices and m the number of the edges. You can assume 2 ≤ n ≤ 100 and 0 ≤ m ≤ n(n − 1)/2. ak and bk (k = 1, …, m) are positive integers less than or equal to n, which represent the two vertices vak and vbk connected by the kth edge ek. wk is a positive integer less than or equal to 10000, which indicates the weight of ek. You can assume that the graph G = (V, E) is simple, that is, there are no self-loops (that connect the same vertex) nor parallel edges (that are two or more edges whose both ends are the same two vertices).

Output
For each dataset, if the graph has spanning trees, the smallest slimness among them should be printed. Otherwise, −1 should be printed. An output should not contain extra characters.

Sample Input

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

Sample Output

1
20
0
-1
-1
1
0
1686
50


首先,本题几乎是一个裸的最小生成树了,但是唯一不同的是“最小”的定义变为了最大最小边的权值差(即本题定义的slimness)最小了。那么,此时Kruskal算法是否仍然适用呢?之前探究过Dijkstra算法和Floyd算法的适用条件,前者只要保持松弛更新有效即可,写过一篇博文讨论这个条件,后者只要满足动态规划条件即可。而Prim算法适用条件类似于Dijkstra算法。Kruskal算法的使用条件探究就要像探究Dijkstra算法一样,从证明手法入手。
Kruskal算法具体证明细节不论,最重要一步是调整,把构造出来的生成树调整成一棵最小生成树,但是代价不变。那么,要用Kruskal算法,只要保持这一过程证明仍然有效即可。也就是说一条边权值变小时,生成树的总代价不能变大,只要存在这个单调性即可。
那么回到本题,slimness似乎不满足上面那个条件,但是只要最小边知道了,那么代价就只与边的最大值有关了(注意这个cost=max{L(e)|e∈G}函数与变式Dijkstra正确性的一个必要条件何其类似!),于是本题算法就是先对边排序,再枚举最小边的长度,用变式Kruskal,把并查集平均时间复杂度看作常数,时间复杂度只有O(e2),而题目中e=O(n2)是一个稠密图似乎不适合用Kruskal算法,也就是说总复杂度高达O(n4),但是n只有100,而且常数很小,而关键在边不在点,所以用变式Kruskal算法。


Accepted    512kB   128ms   1204 B  G++
#define MAX_N 100#define MAX_M ((MAX_N*(MAX_N-1))>>1)#define INF 0x1FFFFFFF#include<stdio.h>#include<stdlib.h>struct edge_type{    int x,y;    int len;};int n,m,ans,e,slim;edge_type edge[MAX_M+1];int root[MAX_N+1]; int compare(const void* p1,const void* p2){    const edge_type* e1=(const edge_type*) p1;    const edge_type* e2=(const edge_type*) p2;    return e1->len-e2->len;}int find(int x){    if (root[x]==x)        return x;    else        return root[x]=find(root[x]);}int main(){    //freopen("input.txt","r",stdin);    while (scanf("%d%d",&n,&m)&&(n+m))    {        ans=INF;        edge[0].len=-1;        for (int i=1;i<=m;i++)            scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].len);        qsort(edge+1,m,sizeof(edge_type),compare);        for (int i0=1;i0<=m;i0++)        {            if (edge[i0].len==edge[i0-1].len)                continue;            for (int i=1;i<=n;i++)                root[i]=i;            e=0;            slim=0;            for (int i=i0;i<=m;i++)                if (find(edge[i].x)!=find(edge[i].y))                {                    root[find(edge[i].x)]=find(edge[i].y);                    e++;                    if (edge[i].len-edge[i0].len>slim)                        slim=edge[i].len-edge[i0].len;                }            if (e==n-1)            {                if (slim<ans)                    ans=slim;             }            else                break;        }        printf("%d\n",(ans==INF)?-1:ans);    }    return 0;}
0 0
原创粉丝点击