poj 3714 Raid (分治+一点鸽巢原理)

题目链接:poj 3714

题意:给个n,首先个n个点的位置,再给n个人的位置,问你,到点最短距离是多少?

参考博客:http://yzmduncan.iteye.com/blog/1432880

https://www.cnblogs.com/captain1/p/9607559.html

题解:

感觉看了这两篇博客,都不需要过多的补充,前人栽的树已经很凉了,但我还是要解释下为什么跟鸽巢原理产生了微妙的联系,为什么?因为博主没解释。首先,我先假设你们都看过这两篇博客,因为照搬进来就没多大意思了。

第二篇博客中有段话:对于C1中的每一个点k,能与之配对更新最短距离的,一定是C2中一个长为dis,高为2*dis的一个矩形之内的点。再者,因为S2中每两个点的距离必须>=dis,所以这个矩形之内最多只可能有6个点

就是这段话,这结论写的真~~~平,一点由来也不说,反正我刚看是不明白为什么最多有6个点,当然可能有人没遇到这个疑问,可以略过不看。

然后细想一下,确实是这样的,我们想下,在这个dis*2dis的矩形中,我们要想每两个点的距离都大于等于dis,那么我们最多放4个点在四个不起眼的角落夹缝生存以及放2个点在两条高的中间,如图所示:

 

图就清楚了,结论就容易出来了,在矩形中,要满足每两个点必须大于等于dis

最多放6个点,在放多一个就不满足了。

这个我们就把它说成是鸽巢定理了。

就差不多是那样了,有“最多”,“至少” 这些字眼。

 

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;
typedef long long LL;

const int maxn=200010;
#define INF 0x3f3f3f3f

struct point{
   double x,y;
    int id;
}node[maxn];

int temp[maxn];

bool cmpxy(point a,point b){
    if(a.x-b.x!=1e-8) return a.x<b.x;
    else return a.y<b.y;
}
bool cmpy(int a,int b){
    return node[a].y<node[b].y;
}

double dist(int a,int b){
    if(node[a].id!=node[b].id) 
    return sqrt((node[a].x-node[b].x)*(node[a].x-node[b].x)+(node[a].y-node[b].y)*(node[a].y-node[b].y));
    else return INF;

}

double solve(int left,int right) ///分治
{
    double d=INF;

    if(left==right) return d;
    if(left+1==right) return dist(left,right);

    int mid=(left+right)/2;

    double d1=solve(left,mid);
    double d2=solve(mid+1,right);

    if(d1<d2) d=d1;
    else d=d2;

    int k=0;
    for(int i=left;i<=right;i++)
        if(fabs(node[mid].x-node[i].x)-d<=1e-8)
            temp[++k]=i;

    sort(temp+1,temp+k+1,cmpy); ///排序,按y

    for(int i=1;i<=k;i++)
    {
        for(int j=i+1;j<=k;j++){
            if(fabs(node[temp[j]].y-node[temp[i]].y)>d) break;
                double d3=dist(temp[i],temp[j]);
            if(d>d3&&node[temp[i]].id!=node[temp[j]].id)
                d=d3;
        }
    }
    return d;

}

int main()
{
    int ncase,n;

    scanf("%d",&ncase);

    while(ncase--)
    {
        int n;
        scanf("%d",&n);

        for(int i=1;i<=n;i++){
            scanf("%lf%lf",&node[i].x,&node[i].y);
            node[i].id=1; ///1表示点位置
        }

        for(int i=n+1;i<=n*2;i++){
            scanf("%lf%lf",&node[i].x,&node[i].y);
            node[i].id=2; ///2表示人的位置
        }


        sort(node+1,node+1+2*n,cmpxy); ///排序,按x,y

        printf("%.3f\n",solve(1,n*2));
    }
    return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值