zoukankan      html  css  js  c++  java
  • 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权重。

  • 相关阅读:
    穿越之我是码农 1024 篇
    误删文件机房停电黑客入侵_你最怕什么?
    AI觉醒进行时:程序员你怕了吗?
    未来已来!阿里小蜜AI技术揭秘
    千人千面智能淘宝店铺背后的算法研究登陆人工智能顶级会议AAAI 2017
    CDN缓存不命中排查
    现实需求巨大_技术尚未成熟_学界与业界思维大碰撞
    围观阿里云最会赚钱的人!价值2万元邀请码不限量发送
    今晚19:30直播阿里巴巴大规模持续集成的技术演进之路_欢迎免费观看
    工作压力山大?码农这么减压最有效
  • 原文地址:https://www.cnblogs.com/OIerPrime/p/8052419.html
Copyright © 2011-2022 走看看