【平面最近点对 分治】 Raid
传送门
题意
\(n\)个核电站的坐标\((x_{1},y_{1})\),\(n\)个特工的坐标\((x_{2},y_{2})\),求所有特工中距离任意一个核电站的一个最短距离,多组数据
数据范围
\(\begin{array}{l}1 \leq N \leq 10^{5} \\ 0 \leq x, y \leq 10^{9}\end{array}\)
题解
最近点对,分治+二分
将所有点按照横坐标排序,折半分治,有三种情况
-
距离最近的两个点都在左边
-
距离最近的两个点都在右边
-
距离最近的两个点一左一右,进行归并
归并的时候:
-
\(d\)表示划分后左边和右边的最小值
-
从\(mid\)以\(d\)为半径向两边各选一个点,两边分别将半圆扩展成一个矩形,两个矩形内部的点的两两之间距离一定分别\(>=d\),只有在其内部的才可能更新答案。
-
从中间开始左右扩散即可,只要横坐标差大于当前取得的最小值其搜索子树就一定不会更新答案,直接剪枝即可
最坏的情况是最近的点分别在最左和最右,这样会退化为\(O(n^{2})\),时间限制为5s依旧可以通过
正常情况下的合并的复杂度是\(O(n)\)递归\(logn\)层,总共时间复杂度是$O(nlogn) $
Code
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<vector>
#include<list>
#include<queue>
#include<string>
#include<set>
#include<map>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define ll long long
const int N=1e6+10;
const double INF=1e10;
int n,_;
struct node
{
int x,y;
bool flag;
bool operator < (const node t)const // 按照横坐标排序
{
return x < t.x;
}
}p[N*2];
double cal(node x, node y)
{
if(x.flag == y.flag)
return INF;
return sqrt((long double)(x.x-y.x)*(x.x-y.x)+(long double)(x.y-y.y)*(x.y-y.y));
}
double dfs(int l,int r)
{
if(l >= r) return INF;
if(l+1 == r) return cal(p[l],p[r]);
int mid = l+r>>1;
double res = min(dfs(l,mid),dfs(mid+1,r));
per(i,mid,l)// 合并中间两段的
{
if(p[mid].x - p[i].x > res) break;
rep(j,mid+1,r+1)
{
if(p[j].x-p[i].x > res) break;
res=min(res,cal(p[i],p[j]));
}
}
return res;
}
void solve()
{
scanf("%d",&n);
rep(i,1,n+1) scanf("%d%d",&p[i].x,&p[i].y),p[i].flag=0;
rep(i,n+1,2*n+1) scanf("%d%d",&p[i].x,&p[i].y),p[i].flag=1;
sort(p+1,p+2*n+1);
printf("%.3lf\n",dfs(1,2*n));
}
int main()
{
scanf("%d",&_);
while(_--) solve();
}

浙公网安备 33010602011771号