题目:
There are towns on a plane. The -th town is located at the coordinates . There may be more than one town at the same coordinates.Ni(xi,yi)
You can build a road between two towns at coordinates and for a cost of yen (the currency of Japan). It is not possible to build other types of roads.(a,b)(c,d)min(|a-c|,|b-d|)。
Your objective is to build roads so that it will be possible to travel between every pair of towns by traversing roads. At least how much money is necessary to achieve this?
Constraints
- 2 ≤ N ≤ 10^5
- 0≤xi,yi≤10^9
- All input values are integers.
Input
Input is given from Standard Input in the following format:
N x1 y1 x2 y2 : xN yN
Output
Print the minimum necessary amount of money in order to build roads so that it will be possible to travel between every pair of towns by traversing roads.
Sample 1
Input | Output |
---|---|
3 1 5 3 9 7 8 |
3 |
Build a road between Towns 1 and 2, and another between Towns 2 and 3. The total cost is 2+1=3 yen.
Sample 2
Input | Output |
---|---|
6 8 3 4 9 12 19 18 1 13 5 7 6 |
8 |
题意:
给你在二维坐标上的N个点,你可以连接任意两点(a,b)(c,d),花费价值为 min(|a-c|,|b-d|)。求所有点连通的最小花费价值。
思路分析:
看题意就是一道最小生成树的问题,但是这题的难点在于如何建图,平常的建图都是一个值代表一个图的结点,但是题目里直接给出了,一个二维的坐标,并且权值各有两种可能,若想先求出所有边权的可能再去建图,时间复杂度无疑太高了。因此我们为了找到差值最小的情况,便可以进行排序,并依照输出的顺序给每个点做标记,分别对x坐标和y坐标分别排序并记录边权最后再对全部的边进行排序使用kruskal算法实现最小生成树。
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<set>
#define IL inline
typedef long long ll;
using namespace std;
//kruskal算法
const int N = 1e5 + 10;
ll n,ans,sum;
struct edge {//记录边的结构体,p1,p2分别记录转换成结点的图,d表示为边的权值
ll p1, p2, d;
} p[2*N];
struct dian {//记录点的结构体 ,以下标id表示为图的结点
ll x, y, id;
} a[N];
bool cmpx(dian a, dian b) {//对x坐标排序
return a.x < b.x;
}
bool cmpy(dian a, dian b) {//对y坐标排序
return a.y < b.y;
}
bool cmp(edge a, edge b) {//对边长坐标排序
return a.d < b.d;
}
int f[N];//记录父节点
int fd(ll x) {//查找父节点
if (x == f[x])return x;
return f[x] = fd(f[x]);
}
void tg(int x, int y) {//合并结点
x = fd(x);
y = fd(y);
if (x != y)f[y] = x;
}
int kruskal() {//算法实现
int num=0;
for (int i = 1; i <= ans; i++) {
int x=fd(p[i].p1),y=fd(p[i].p2);
if(x==y)continue;
f[x]=y;
sum+=p[i].d;
num++;
if(num==n-1)break;
}
return sum;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%lld%lld", &a[i].x, &a[i].y);//输入点的坐标
a[i].id = i;//记录其为图的结点
f[i] = i;//初始化父节点
}
sort(a + 1, a + n + 1, cmpx);//对x排序
for (int i = 1; i < n; i++) {//分别记录两个结点的x的所表示边的权值
p[++ans].p1 = a[i].id;
p[ans].p2 = a[i + 1].id;
p[ans].d = abs(a[i].x - a[i + 1].x);
}
sort(a + 1, a + n + 1, cmpy);
for (int i = 1; i < n; i++) {//分别记录两个结点的y的所表示边的权值
p[++ans].p1 = a[i].id;
p[ans].p2 = a[i + 1].id;
p[ans].d = abs(a[i].y - a[i + 1].y);
}
sort(p+1,p+ans+1,cmp);//对其边的全部权值进行排序 ,转换为了kruskal求最小生成树问题
cout<<kruskal()<<endl;
return 0;
}