Building Roads to Connect Cities

Kruskal求最小生成树

May 17, 2017

题目链接:pdf

给出平面上的一些点,任意两点之间可以相连,求最小生成树的总权值。

用Kruskal:把所有边按照边长度升序排序,循环所有边,每次加入一个边之前要判断这个边的两个点是不是在一个集合中,如果是那么跳过,如果不是那么把这个边的两个点合并到一个集合中。集合操作用带路径压缩的并查集。循环边的过程中累加边的权值。

// Tue May 16 17:02:04 2017
// Tue May 16 20:42:10 2017

#include <bits/stdc++.h>

using namespace std;

struct edge {
  int from, to;
  double cost;
};

inline double p_distance(int x1, int y1, int x2, int y2) {
  return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

int p_find(int x, vector<int> &parent) {
  int p = x;
  
  while (p != parent[p]) {
    p = parent[p];
  }
  return parent[x] = p;
}

void p_union(int x, int y, vector<int> &parent, vector<int> &p_size) {
  int p_x = p_find(x, parent), p_y = p_find(y, parent), ps_x = p_size[p_x],
    ps_y = p_size[p_y];
  
  if (ps_x > ps_y) {
    parent[p_y] = p_x;
    p_size[p_x] += ps_y;
  }
  else {
    parent[p_x] = p_y;
    p_size[p_y] += ps_x;
  }
}					       

double minimum_distance(vector<int> x, vector<int> y) {
  double result = 0.;
  vector<edge> edges;

  for (size_t i = 0; i < x.size(); ++i) {
    for (size_t j = i + 1; j < x.size(); ++j) {
      edge e; e.from = i; e.to = j; e.cost = p_distance(x[i], y[i], x[j], y[j]);
      edges.push_back(e);
    }
  }

  sort(edges.begin(), edges.end(),
       [](const edge &a, const edge &b) -> bool { return a.cost < b.cost; });
  vector<int> parent(x.size()), p_size(x.size());

  for (size_t i = 0; i < parent.size(); ++i) {
    parent[i] = i;
    p_size[i] = 1;
  }

  for (auto &e : edges) {
    if (p_find(e.from, parent) != p_find(e.to, parent)) {
      p_union(e.from, e.to, parent, p_size);
      result += e.cost;
    }
  }

  return result;
} 

int main() {
  
#ifdef DEBUG
  freopen("in", "r", stdin);
#endif
  
  size_t n;
  
  while(cin >> n) {
    vector<int> x(n), y(n);
    
    for (size_t i = 0; i < n; i++) {
      std::cin >> x[i] >> y[i];
    }
    
    std::cout << std::setprecision(10) << minimum_distance(x, y) << std::endl;
  }
}

comments powered by Disqus