[USACO07DEC]Building Roads S
题目描述
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.
给定 n n n 个点的坐标,第 i i i 个点的坐标为 ( x i , y i ) (x_i,y_i) (xi,yi),这 n n n 个点编号为 1 1 1 到 n n n。给定 m m m 条边,第 i i i 条边连接第 u i u_i ui 个点和第 v i v_i vi 个点。现在要求你添加一些边,并且能使得任意一点都可以连通其他所有点。求添加的边的总长度的最小值。
输入格式
* 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.
第一行两个整数
n
,
m
n,m
n,m 代表点数与边数。
接下来
n
n
n 行每行两个整数
x
i
,
y
i
x_i,y_i
xi,yi 代表第
i
i
i 个点的坐标。
接下来
m
m
m 行每行两个整数
u
i
,
v
i
u_i,v_i
ui,vi 代表第
i
i
i 条边连接第
u
i
u_i
ui 个点和第
v
i
v_i
vi 个点。
输出格式
* 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.
一行一个实数代表添加的边的最小长度,要求保留两位小数,为了避免误差, 请用 64 64 64 位实型变量进行计算。
样例 #1
样例输入 #1
4 1
1 1
3 1
2 3
4 3
1 4
样例输出 #1
4.00
提示
数据规模与约定
对于 100 % 100\% 100% 的整数, 1 ≤ n , m ≤ 1000 1 \le n,m \le 1000 1≤n,m≤1000, 1 ≤ x i , y i ≤ 1 0 6 1 \le x_i,y_i \le 10^6 1≤xi,yi≤106, 1 ≤ u i , v i ≤ n 1 \le u_i,v_i \le n 1≤ui,vi≤n。
说明
Translated by 一只书虫仔。
思路:
由题意可知,是用最小生成树的方法。因为这道题给的是点与点之间的坐标,没有直接给出权值,所以首先可以先预处理出每个点到另外一个点的距离,然后在输入点与点之间的关系的时候将这两个点之间的距离置0。接下来的算法就和普通的最小生成树的算法一样了。
**注意:**这里的edge数组要开到 106,因为 n 最大能到 1000
代码实现:
// Kruskal 算法求最小生成树
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
const int MAXN = 1e6+10;
int fa[MAXN], cnt = 0;
struct node
{
int u, v;
double w;
}edge[MAXN];
struct node1
{
int x, y;
}V[MAXN];
void add(int a, int b, double c)
{
cnt++;
edge[cnt].u = a;
edge[cnt].v = b;
edge[cnt].w = c;
//cnt++;
}
bool cmp(node a, node b)
{
return a.w < b.w;
}
// 利用并查集的思想,将每个顶点放入一个集合中
int find(int x)
{
if(fa[x] == x) return x;
fa[x] = find(fa[x]);
return fa[x];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n, m;
double sum = 0;
cin >> n >> m;
for(int i=1;i<=n;i++)
{
cin >> V[i].x >> V[i].y;
}
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
double S = sqrt((double)(V[i].x-V[j].x)*(double)(V[i].x-V[j].x)+(double)(V[i].y-V[j].y)*(double)(V[i].y-V[j].y));
add(i, j, S);
}
}
// 已连接的边之间花费设为0
for(int i=1;i<=m;i++)
{
int a, b;
cin >> a >> b;
add(a, b, 0.0);
}
for(int i = 0; i <= n; i ++)
{
fa[i] = i;
}
// 对边进行排序
sort(edge + 1, edge + 1 + cnt, cmp);
// 每次加入一条最短的边
for(int i = 1; i <= cnt; i ++)
{
int x = find(edge[i].u);
int y = find(edge[i].v);
// 如果已经在同一个集合中了,就不加入
if(x == y) continue;
fa[y] = x;
sum += edge[i].w;
}
printf("%.2lf", sum);
return 0;
}