题解 P2872 【[USACO07DEC]道路建设Building Roads】

这道题真的是令人窒息,Kruskal调了贼久一直RE,最后发现数组大小稍微少了那么一点点。(也就10倍吧。。)

言归正传,根据本人的分析(以及算法标签的提示),这是一道求最小生成树的题目,当然要注意已经有一些路被建成了,因此他们直接标0即可

下面是这道题用到了的所有(全局)变量。

maxn, n, m就不解释了。

x[]和y[]是用来储存农场的坐标的,当然也可以用二维数组写,只是我懒得敲那么多字(说起来差别也不大)。

f是并查集中储存祖先的数组。

如果有不了解的并查集的可以参考这一片讲解,个人认为十分通俗易懂。 传送门

f1和f2是后面暂时储存有道路连接的农场的变量。

top是Kruskal主体中记录最顶层的。

cnt是记录长度的。

ans我觉得也是废话(耶,皮这一下我很开心)。

1 const int maxn = 1000001;
2 
3 int n, m;
4 int x[maxn], y[maxn], f[maxn], f1, f2;
5 int top = 0, cnt = 0;
6 double ans = 0;

接下来是储存两点距离的结构体,以及结构体排序。

 1 struct node {
 2     int x, y;
 3     double val; 
 4 }dis[maxn];
 5 
 6 bool cmp(node a, node b) {
 7     if(a.val == b.val)
 8         return a.x < b.x;
 9     return a.val < b.val;  
10 }

以及并查集模板(其中find函数使用了路径压缩)

 1 int find(int x) {  
 2     int r = x;  
 3     while(r != f[r]) r = f[r];  
 4     int i = x, j;  
 5     while(f[i] != r) {  
 6         j = f[i];  
 7         f[i] = r;  
 8         i = j;  
 9     }  
10     return r;  
11 }  
12 
13 void merge(int x, int y) {
14     x = find(x);
15     y = find(y);
16     if(x != y) f[y] = x;
17 }

 

偶对了还有最没用的函数dt,用于求两点距离。

1 double dt(int x1,int x2,int y1,int y2) {
2     return sqrt(pow(double(x1 - x2), 2) + pow(double(y1 - y2), 2));
3 }

接下来果断开始主函数part。

读入数据,别忘了初始化并查集。

 1 cin >> n >> m;
 2 for(int i = 1; i <= n; i++) cin >> x[i] >> y[i];
 3 for(int i = 1; i <= n; i++) f[i] = i;
 4 for(int i = 1; i <= n; i++)
 5     for(int j = i + 1; j <= n; j++) {
 6         dis[++cnt].x = i;
 7         dis[cnt].y = j;
 8         dis[cnt].val = dt(x[i], x[j], y[i], y[j]);
 9     }
10 for(int i = 1; i <= m; i++) {
11     cin >> f1 >> f2;
12     dis[++cnt].x = f1;
13     dis[cnt].y = f2;
14     dis[cnt].val = 0;
15 }

然后给dis排个序。

 1 sort(dis + 1, dis + cnt + 1, cmp); 

最重要的部分:Kruskal模板

Kruskal算法将图中的每一个顶点视为一个独立的集合,首先将图中所有的边按权值大小从小到大排列,接着按顺序选择每一条边,如果这条边的两个端点不属于同一个集合,那么将它们所在的集合合并,同时将这条边加入E’。直到所有的顶点都属于同一个集合时,E’就是一颗最小生成树。

--摘抄自《ACM国际大学生程序设计竞赛 知识与入门》

1 for(int i = 1; i <= cnt; i++) {
2     if(find(dis[++top].x) != find(dis[top].y)) {
3         ans += dis[top].val;
4         merge(dis[top].x, dis[top].y);
5     }
6 }

最后,愉快的输出结果就好了,别忘了要保留两位小数。

原文地址:https://www.cnblogs.com/ilverene/p/Luogu2872.html