欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[poj3714]Raid

程序员文章站 2022-05-09 10:38:25
...

题目描述

After successive failures in the battles against the Union, the Empire retreated to its last stronghold. Depending on its powerful defense system, the Empire repelled the six waves of Union's attack. After several sleepless nights of thinking, Arthur, General of the Union, noticed that the only weakness of the defense system was its energy supply. The system was charged by N nuclear power stations and breaking down any of them would disable the system.

The general soon started a raid to the stations by N special agents who were paradroped into the stronghold. Unfortunately they failed to land at the expected positions due to the attack by the Empire Air Force. As an experienced general, Arthur soon realized that he needed to rearrange the plan. The first thing he wants to know now is that which agent is the nearest to any power station. Could you, the chief officer, help the general to calculate the minimum distance between an agent and a station?

题目大意

超出两个点对集合之间最接近的两个点对的距离

解法

一开始还不知道是用分治来做,我太弱了。但是我又想了一下,如果将这个玩意投射到二维线段树上不是特别好做吗,虽然很明显会\(T\)

那么我就参照了线段树的思想,这就是我最后想到分支做法的思维过程。

将我们当前访问的区间设为\([l,r]\)

  • 如果 r=l+1 那么可以肯定的是,这个区间只有两个点了,那么就直接算出两代你的距离。

  • 如果 r=l+2 那么这个区间里面也就只有3个点,那么也就可以直接算出答案。

那么接下来就是剩下来的情况。

我们假定\([l,mid]\)\([mid+1,r]\)这两个区间已经算好答案。(毕竟我们是递归回来算较大的答案)

如果这个答案是0,那么就说明有两个点是靠在一起了,那么就直接退出输出0就可以了。

但是我们需要筛选出所有可能的答案,那么我们就需要从\(mid\)开始,向两边搜索,条件就是\(x\)坐标和\(mid\)\(x\)坐标的差值小于我们之前算出的答案,因为这些点之间都有可能成为最小答案,但是在范围外的点一定不是最优的答案,因为到\(mid\)的水平距离都大于我们算出的答案,如果算上\(y\)坐标,一定是更加大的答案。那么这些点都有可能和对面的点成为最优的答案。

那么我们筛选完了点,就是计算的问题,计算比较简单,也就是去距离最小的。

ac代码

#include<cstdio>
#include<algorithm>
#include<cmath>
#define Inf 1e100
#define dd double 
#define N 200005
using namespace std;
struct node{
    dd x,y;
    int t;
    bool operator <(const node &rhs)const{
        return x<rhs.x;
    }
}a[N];
int q[N];
int n;
inline dd calc(int n,int m){//计算距离 
    if(a[n].t!=a[m].t) return sqrt((a[n].y-a[m].y)*(a[n].y-a[m].y)+(a[m].x-a[n].x)*(a[m].x-a[n].x));
    else return Inf;
}
inline dd solve(int l,int r){
    if(r-l==1) return calc(l,r);//直接算答案 
    else if(r-l==2) return min(min(calc(l,l+1),calc(l+1,r)),calc(l,r));//直接算答案 
    int mid=(l+r)>>1;
    dd res=min(solve(l,mid),solve(mid+1,r));//递归求解 
    if(res==0) return 0;//发现重合的点 
    int tot=0; 
    for(int i=mid;a[mid].x-a[i].x<res&&i>=l;i--) q[++tot]=i;//筛选左边的点 
    for(int i=mid+1;a[i].x-a[mid].x<res&&i<=r;i++) q[++tot]=i;//筛选右边的点 
    sort(q+1,q+1+tot);//排序便于计算 
    for(int i=1;i<=tot;i++){//计算答案 
        for(int j=i+1;j<=tot;j++){
            if(a[q[j]].y-a[q[i]].y>=res) break;
            res=min(res,calc(q[i],q[j]));
        }
    }
    return res;
}
int main(){
    int cas;
    scanf("%d",&cas);
    while(cas--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%lf%lf",&a[i].x,&a[i].y);
            a[i].t=0;
        }
        for(int i=n+1;i<=2*n;i++){
            scanf("%lf%lf",&a[i].x,&a[i].y);
            a[i].t=1;
        }
        sort(a+1,a+1+2*n);//排序便于计算 
        printf("%.3f\n",solve(1,2*n));//计算答案 
    }
    return 0;
}