USACO 2007 December Contest, Silver Problem 2. Building Roads Kruskal最小生成树算法

PROBLEM:

(ENGLISH VERSION)

Farmer John had just acquired several new farms! He wants to connect the farms with roads so that he can travel from any farm to any other farm via a sequence of roads; roads already connect some of the farms.

Each of the N (1 ≤ N ≤ 1,000) farms (conveniently numbered 1..N) is represented by a position (Xi, Yi) on the plane (0 ≤ Xi ≤ 1,000,000; 0 ≤ Yi ≤ 1,000,000). Given the preexisting M roads (1 ≤ M ≤ 1,000) as pairs of connected farms, help Farmer John determine the smallest length of additional roads he must build to connect all his farms.

INPUT FORMAT:

Line 1: Two space-separated integers: N and M

Lines 2..N+1: Two space-separated integers: Xi and Yi

Lines N+2..N+M+2: Two space-separated integers: i and j, indicating that there is already a road connecting the farm i and farm j.

OUTPUT FORMAT:

Line 1: Smallest length of additional roads required to connect all farms, printed without rounding to two decimal places. Be sure to calculate distances as 64-bit floating point numbers.

SAMPLE INPUT

4 1
1 1
3 1
2 3
4 3
1 4

SAMPLE OUTPUT

4.00

USACO ANALYSIS

USACO DEC07 Problem 'roads' Analysis

by Richard Peng

We note that since all edges have non-negative weights, there will not be a cycle in the final version of the graph. Thus, this problem is equivalent to finding the minimum spanning tree in a graph where the edge weights are the Euclidean distances (with the exception of a few whose distances are set to zero).

Several minimum spanning tree algorithms can be use here. Since we're finding the MST of a dense graph, the best option is probably the O(n^2) version of the Prim algorithm:

  • Start with the tree being a single vertex
  • Keep a list of distances of every other vertex to the tree
  • At each iteration
    • Add the closest vertex to the tree
    • Update the distances accordingly.

This runs in O(n^2) time, which suffices for this problem.

By the way: This problem can actually be done in O(nlogn+m) time. The idea is basically the edges that could potentially be in the minimum spanning tree must belong to what's known as the Delaunay triangulation, which has O(n) edges. We can find the Delaunary triangulation in O(nlogn) time and apply a fast version of Kruskal's algorithm for sparse graphs to get the desired runtime.

程序

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int MAX = 1000 + 1;
 4 struct node
 5 {
 6     int From, Aim;
 7     long double Weight;
 8 }Edge[MAX*MAX];
 9 bool have[MAX][MAX];
10 int n,m,x[MAX],y[MAX],Count = 0,father[MAX];
11 long double ans = 0;
12 long double EdgeLength(int pointA, int pointB)
13 {
14     long long DiffX=x[pointA]-x[pointB], DiffY=y[pointA]-y[pointB];
15     return (long double)sqrt(DiffX*DiffX+DiffY*DiffY);
16 }
17 bool comp(node a, node b)
18 {
19     return a.Weight < b.Weight;
20 }
21 int find(int i)
22 {
23     if(father[i]==i)
24         return i;
25     return father[i]=find(father[i]);
26 }
27 void join(int a,int b)
28 {
29     if(find(a)==find(b))
30         return;
31     father[find(a)]=b;
32 }
33 int main()
34 {
35     //Kruskal + Disjoint-set
36     memset(have,0,sizeof(have));
37     cin >> n >> m;
38     for (int i = 1; i <= n; i++)
39         cin >> x[i] >> y[i], father[i] = i;
40     int a,b;
41     for (int i = 1; i <= m; i++)
42     {
43         cin >> a >> b;
44         have[a][b] = have[b][a] = true;
45         Edge[++Count] = (node){a,b,0};
46     }
47     for(int i = 1; i <= n; i++)
48         for(int j = i+1; j <= n; j++)
49             if(!have[i][j])
50                 Edge[++Count] = (node){i,j,EdgeLength(i,j)};
51     sort(Edge+1, Edge+(Count+1), comp);
52     //Disjoint-set
53     for(int i = 1; i <= Count; i++)
54         if(find(Edge[i].From) != find(Edge[i].Aim))
55         {
56             join(Edge[i].From, Edge[i].Aim);
57             ans += Edge[i].Weight;
58         }
59     printf("%.2Lf",ans);
60     return 0;
61 }

分析

本题的卡点就是已经存在的边为什么权重要设为0。简单来说,这条路已经存在相当于建造这条路的花费为0。(代码中红色的0)

其余的就没有什么难度了,简单套用Kruskal模板。

把所有点的连线及其长度存在数组Edge中,用一个变量Count计数。结构体node中From是边的一端,另一端是Aim。这条边的权重,也就是平面直角坐标系中用两点间距离公式求出的长度(请参见初中课本xd)。Kruskal的核心就是进行排序,按照权重大小进行排序。最后按照权重从小到大选边,只要不会产生环,就选择这条边。放到并查集中,就是它们拥有不同的爸爸祖先,就选择这条边。然后累加Weight权重。

原文地址:https://www.cnblogs.com/OIerPrime/p/8052419.html