• Raid


    Raid

    给出大小为n点集(p_1),第i个点记做((x_i,y_i)),给出另外一个大小为n的点集(p_2),记第i个点的坐标为(a_i,b_i),现在你可以从两个点集中各选出一个点,使得两点间距离最小化,求出这个最小值,(nleq 100000)

    显然想到了平面最近点对,区别在于有两个点集,按照一样的方法,接下来简单讲一讲,先把两个点集合并成一个大小为(2n)的点集以后,记第i个点为((x_i,y_i,b_i)),这个三元组前两元代表坐标,后面的(b_i)表示所属点集(不妨0表示属于(p_1),1属于(p_2))。

    把所有点按照x轴坐标从小到大排序,设(d(l,r))表示处理的点编号从(lsim r)的最小值,现在考虑如何求(d(l,r)),选定一个中间点,记做(mid=l+r>>1),然后就可以对递归下去,处理出(d(l,mid),d(mid+1,r)),对两者取min后记做(ans)

    现在的问题就变作如何求出(lsim mid)间的点和(mid+1sim r)间的点最小值,不妨将这两个点集分别记做(q_1,q_2),注意到可以利用之前求出的ans剪枝,于是我们就只要对x轴坐标在((x_{mid}-ans,x_{mid}+ans))间的点进行处理,此时记这个由(x=x_{mid})划分成的两个点集为(w_1,w_2),那么对于(w_1)中的每个点而言,只有(w_2)中的点能和它构成最优解,而且还要满足(w_2)中的点与该点的y轴方向上的距离不能超过ans。

    因此,在最差情况下,(w_1)中的点,不妨编号记做j,点j就在(x=a_{mid})上,以它为中心可以向上延伸ans的距离,向下延伸ans的距离,向右延伸ans的距离,构成了一个长2ans,宽ans的长方形,显然里面的点最多只有6个,因为再多一个无论如何都会与它所属的点集中的一个点距离小于ans,这也不满足我们的ans为(q_1,q_2)分开来看的最小值。

    因此哪怕(q_1)的点(显然夸张了)全部在(w_1)中,我们也只要对于每个点枚举至多5个点,也就是(n/2 imes 5approx n)个点,而递归只有(logn),因此时间复杂度最好可以做到(nlog(n))

    为了便于找出一个点y方向上的满足条件的点,最自然的想法时提出(w_1,w_2)里的点,按照y轴坐标排序,然后就可以做到常数时间复杂度求最小值,也就是总时间复杂度(nlog(n)^2)

    但是注意到x方向上的排序,我们也只用了一次,也就是快速求出mid,不妨记录下mid这个点,然后递归下去,回来时按照归并排序的套路,把y轴坐标排序,然后再从左至右暴力枚举每个点可以在(w_1,w_2)中,此时得到的顺序就是y轴坐标递增的,因此我们做到了(nlog(n))

    最后注意,只有属于不同的点集(p_1,p_2)才能计算答案,一定要特判。

    参考代码:

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #define il inline
    #define ri register
    #define double long double
    #define Size 200050
    using namespace std;
    struct pos{
    	double x,y;bool id;
    	il bool operator<(const pos&a)const{
    		return x<a.x;
    	}
    }p[Size],t[Size];
    il double divide(int,int),dis(pos,pos);
    template<class free>il free Abs(free);
    template<class free>il free Min(free,free);
    int main(){
    	int lsy;scanf("%d",&lsy);
    	while(lsy--){int n;scanf("%d",&n);
    		for(int i(1);i<=n;++i)scanf("%Lf%Lf",&p[i].x,&p[i].y),p[i].id=0;
    		for(int i(2*n);i>n;--i)scanf("%Lf%Lf",&p[i].x,&p[i].y),p[i].id=1;
    		sort(p+1,p+2*n+1),printf("%.3Lf
    ",divide(1,2*n));
    	}
    	return 0;
    }
    il double dis(pos a,pos b){
    	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    template<class free>
    il free Abs(free x){
    	return x<0?-x:x;
    }
    template<class free>
    il free Min(free a,free b){
    	return a<b?a:b;
    }
    il double divide(int l,int r){if(l==r)return 1e16;int mid(l+r>>1);
    	double mx(p[mid].x),ans(Min(divide(l,mid),divide(mid+1,r)));
    	int i(l),j(mid+1),k(l),tot(0);
    	while(i<=mid&&j<=r)
    		if(p[i].y<p[j].y)t[k++]=p[i++];
    		else t[k++]=p[j++];
    	while(i<=mid)t[k++]=p[i++];
    	while(j<=r)t[k++]=p[j++];
    	for(i=l;i<=r;++i)p[i]=t[i];
    	for(i=l;i<=r;++i)
    		if(Abs(p[i].x-mx)<ans)t[++tot]=p[i];
    	for(i=1;i<=tot;++i)
    		for(j=i+1;j<=tot;++j){
    			if(t[j].y-t[i].y>=ans)break;
    			if(t[i].id^t[j].id)ans=Min(ans,dis(t[i],t[j]));
    		}return ans;
    }
    
    
  • 相关阅读:
    数组
    做了个进制转换图
    类的练习
    3.10l练习
    c#学习第二课
    c#第四课习题
    c#学习第三课
    学习PHP&MYSQL之——字符编码篇(一)
    中缀表达式转换成后缀表达式
    模板方法模式(Template Pattern)
  • 原文地址:https://www.cnblogs.com/a1b3c7d9/p/11221035.html
Copyright © 2020-2023  润新知