1. 程式人生 > >poj 3714 Raid (分治+一點鴿巢原理)

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;
}