洛谷题单指南-分治与倍增-P1257 平面上的最接近点对
原题链接:https://www.luogu.com.cn/problem/P1257
题意解读:
解题思路:
1、暴力法
枚举每两个点,更新距离最短值
2、分治法
将点按x坐标排序,a[i].x,a[i].y表示第i个点的x,y坐标
将点划分为两部分,设左半部分为A,右半部分为B
两个距离最短的点有三种可能:两个点都在A;两个点都在B;一个点在A,一个点在B。
因此,可以采用分治法求解!
设left是第一个点编号,right是最后一个点编号
中间点mid = (left + right) / 2
对两部分分别计算最短距离,对部分递归求解:left~mid,mid+1~right,得到左边两点最短距离minleft,右边两点最短距离minright
再考虑第三种情况,一个点在左边、一个点在右边的情况:
d = min(minleft, minright)
左、右两边的点只可能在a[mid].x - d ~ a[mid].x + d的范围内,因为左右两边的点距离如果超出d就没有意义
将a[mid].x - d ~ a[mid].x + d范围内的点按y坐标排序
计算是否有两个点距离比d小,更新最小值。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 10005;
struct node
{
int x, y;
} a[N], tmp[N];
int n;
bool cmpx(node &a, node &b)
{
return a.x < b.x;
}
bool cmpy(node &a, node &b)
{
return a.y < b.y;
}
double dist(node &a, node &b)
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
double min_dist(int left, int right)
{
if(left == right) return 2e9;
if(left + 1 == right) return dist(a[left], a[right]);
int mid = (left + right) / 2;
double leftmin = min_dist(left, mid);
double rightmin = min_dist(mid + 1, right);
double d = min(leftmin, rightmin);
int cnt = 0;
for(int i = left; i <= right; i++)
{
if(fabs(a[i].x - a[mid].x) <= d)
tmp[++cnt] = a[i];
}
sort(tmp + 1, tmp + cnt + 1, cmpy);
for(int i = 1; i < cnt; i++)
{
for(int j = i + 1; j <= cnt && fabs(tmp[i].y - tmp[j].y) < d; j++)
{
d = min(d, dist(tmp[i], tmp[j]));
}
}
return d;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i].x >> a[i].y;
sort(a + 1, a + n + 1, cmpx);
double ans = min_dist(1, n);
cout << fixed << setprecision(4) << ans;
return 0;
}