CodeVS2319 最近最远点对

http://codevs.cn/problem/2319/

题意:给定笛卡尔平面内的若干个点,求最近两点的距离和最远两点的距离,距离是欧氏距离,点数不大于100,000。

对于最近点对考虑分治的做法:用一条直线将所有点分成两部分,对于两部分的点进行分治,然后考虑两部分联合的结果。

可以先把所有点按纵坐标排序,这样就容易用平行于x轴的直线划分。设两边分别求出的结果的较小值是d,我们只需考虑直线两侧离直线距离d以内的所有点即可。

考虑枚举直线某一侧的所有点,对于每个点,在直线另一侧到该点距离小于d的点,是有常数性的限度的。

假设这个点离直线非常非常近,或者就在直线上,以该点为圆心,d为半径所作出的圆在直线另一侧的部分是一个劣弓形,或者半圆。

直线另一侧的一个点若要到该点的距离小于d,则必须落在这个弓形或半圆以内。一个小小的半圆里面能有多少个点呢?注意到在这些点中,所有点对的距离不小于d!

最多只有5个点。事实上,具体算法实现非常简单。对前一半点和后一半点进行分治,然后两重循环枚举前一半点和后一半中的前五个点,计算距离并更新即可。

对于最远点对,显然答案中的两个点一定在整个点集的凸包上,凸包可以用Graham算法来求出。然后旋转卡壳即可。

关于旋转卡壳,我们这里要找的就是在凸包上,与每个点距离最远的另一个点,设凸包上的若干个点依次是p[]号点,离p[i]号点最远的点是f[i]号点。

则对于顺时针依次的p[i]和p[i+1],其答案f[i]和f[i+1]也一定按顺时针排列,这是比较显然的,于是可以用单调队列来求出f[],选出最远的一对即可。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
#include<cmath>
#include<climits>
#define rpt(i,l,r) for(i=l;i<=r;i++)
#define rpd(i,r,l) for(i=r;i>=l;i--);
#define maxn 100005
using namespace std;
struct point{
       double x,y;
};
point a[maxn],b[maxn*2];
int n,i;
double ans1=INT_MAX,ans2=0;
bool cmp1(point A,point B){
     return A.y<B.y;
}
bool clockwise(point A,point B,point C){
     return A.x*B.y+B.x*C.y+C.x*A.y-A.y*B.x-B.y*C.x-C.y*A.x<0;
}
bool cmp2(point A,point B){
     return clockwise(A,a[1],B);
}
double dist(point A,point B){
       double distx=A.x-B.x,disty=A.y-B.y;
       return sqrt(distx*distx+disty*disty);
}
void input(){
     scanf("%d",&n);
     rpt(i,1,n) scanf("%lf%lf",&a[i].x,&a[i].y);
}
void solve1(int l,int r){
     if(l<r){
             int m=l+r>>1,i,j,k;
             double tmp;
             solve1(l,m);
             solve1(m+1,r);
             rpt(i,l,m) rpt(j,m+1,min(m+5,r)){
                        tmp=dist(a[i],a[j]);
                        if(tmp<ans1) ans1=tmp;
             }
     }
}
void solve2(){
     int k=2,j=1;
     double tmp;
     b[1]=a[1],b[2]=a[2];
     rpt(i,3,n){
                while(k>1&&clockwise(b[k-1],b[k],a[i])) k--;
                b[++k]=a[i];
     }
     rpt(i,1,k) b[k+i]=b[i];
     rpt(i,1,k){
                while(dist(b[i],b[j])<dist(b[i],b[j+1])+1e-6) j++;
                tmp=dist(b[i],b[j]);
                if(tmp>ans2) ans2=tmp;
     }
}
int main(){
    input();
    sort(a+1,a+n+1,cmp1);
    solve1(1,n);
    sort(a+2,a+n+1,cmp2);
    solve2();
    printf("%.2f %.2f\n",ans1,ans2);
    return 0;
}

你可能感兴趣的:(CodeVS2319 最近最远点对)