[poj3714]Raid

本文介绍了一种使用分治策略解决点集中最近点对问题的方法,通过将问题分解并递归求解,最终筛选出所有可能的最近点对候选,计算并返回最小距离。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值