[poj3714]Raid

题目描述

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

转载于:https://www.cnblogs.com/chhokmah/p/10427670.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值