poj 3714 Raid 分治

先按横坐标排序,然后分治,合并的时候只需要处理两边x坐标差在d范围内的点即可。把这些点找出来之后,再按照y值排序,然后再检查y坐标差在d以内的点,那么可以保证每个点最多检查6个点(否则这些点之间必定存在距离小于d的点对),那么可以保证o(n)的合并。总的复杂度nlognlogn。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=1e5+9;
const double inf=1e11;
struct D
{
    int x,y,id;
    bool operator <(const D & xx) const
    {
        return x<xx.x;
    }
}data[maxn<<1],now[maxn<<1];

struct cmp
{
    bool operator()(D a,D b)
    {
        return a.y<b.y;
    }
};

double cal(int t,int s)
{
    if(now[t].id==now[s].id) return inf;
    double a=now[t].x-now[s].x;
    double b=now[t].y-now[s].y;
    a*=a;
    b*=b;
    return sqrt(a+b);
}

double solve(int l,int r)
{
    if(l==r) return inf;
    int mid=l+r>>1;
    double ret=solve(l,mid);
    ret=min(ret,solve(mid+1,r));
    int lon=0;
    for(int i=mid;i>=l;i--)
    {
        if(data[mid+1].x-data[i].x>ret) break;
        now[++lon]=data[i];
    }
    for(int j=mid+1;j<=r;j++)
    {
        if(data[j].x-data[mid].x>ret) break;
        now[++lon]=data[j];
    }
    sort(now+1,now+1+lon);
    for(int i=1;i<=lon;i++)
    for(int j=i+1;j<=lon;j++)
    {
        if(now[j].y-now[i].y>ret) break;
        ret=min(ret,cal(i,j));
    }
    return ret;
}

int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n+n;i++)
        {
            scanf("%d %d",&data[i].x,&data[i].y);
            data[i].id=i>n;
        }
        sort(data+1,data+1+n+n);
        double ans=solve(1,n+n);
        printf("%.3f\n",ans);
    }
    return 0;
}


 

你可能感兴趣的:(poj 3714 Raid 分治)