1. 程式人生 > >USACO 2007 December Contest, Silver Problem 2. Building Roads Kruskal最小生成樹算法

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

distance existing 其余 via output update 4.0 核心 decimal

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權重。

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