A.袭击(平面最近点对)
题目描述
在与联盟的战斗中屡战屡败后,帝国撤退到了最后一个据点。
依靠其强大的防御系统,帝国击退了联盟的六波猛烈进攻。
经过几天的苦思冥想,联盟将军亚瑟终于注意到帝国防御系统唯一的弱点就是能源供应。
该系统由N个核电站供应能源,其中任何一个被摧毁都会使防御系统失效。
将军派出了N个特工进入据点之中,打算对能源站展开一次突袭。
不幸的是,由于受到了帝国空军的袭击,他们未能降落在预期位置。
作为一名经验丰富的将军,亚瑟很快意识到他需要重新安排突袭计划。
他现在最想知道的事情就是哪个特工距离其中任意一个发电站的距离最短。
你能帮他算出来这最短的距离是多少吗?
输入格式
输入中包含多组测试用例。
第一行输入整数T,代表测试用例的数量。
对于每个测试用例,第一行输入整数N。
接下来N行,每行输入两个整数X和Y,代表每个核电站的位置的X,Y坐标。
在接下来N行,每行输入两个整数X和Y,代表每名特工的位置的X,Y坐标。
输出格式
每个测试用例,输出一个最短距离值,结果保留三位小数。
每个输出结果占一行。
solution:
分治+二分(最近点对问题)
算法分析:这是一道经典的最近点对问题的模板,这里略微讲述一下这种问题的解法,首先呢,我们将这些点的 x x x坐标,为第一关键字, y y y坐标为第二关键字,从小到大排序,接着我们取一个中点 m i d mid mid点,将 m i d mid mid点左边的点,统统归为平面 d 1 d1 d1,然后 m i d mid mid点右边的点统统归为平面 d 2 d2 d2如下图所示。
然后在如图所示的左右均 d / 2 d/2 d/2区域中,再按 y y y为关键字排序。再暴力枚举。注意可以剪枝。
时间复杂度是 O ( n l o g n l o g n ) O(nlognlogn) O(nlognlogn)。优化主要是在跨区间的寻找上,可以省掉不少的枚举时间。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int inf=0x3f3f3f3f;
inline int read()
{
int X=0; bool flag=1; char ch=getchar();
while(ch<'0'||ch>'9') {
if(ch=='-') flag=0; ch=getchar();}
while(ch>='0'&&ch<='9') {
X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
if(flag) return X;
return ~(X-1);
}
struct node {
double x,y;
int f;
}a[N],b[N],t[N];
double dist(int l,int r) {
if(a[l].f==a[r].f) return inf;
return sqrt((a[l].x-a[r].x)*(a[l].x-a[r].x)+(a[l].y-a[r].y)*(a[l].y-a[r].y));
}
int T,n;
bool cmp(node A,node B) {
return A.y<B.y;
}
bool cmp2(node A,node B) {
return A.x<B.x;
}
double dfs(int l,int r) {
if(l+1>=r) return inf;
int mid=(l+r)>>1,cnt=0;
double res=min(dfs(l,mid),dfs(mid,r));
int i=l,j=mid,num=l;
while(i<mid||j<r) {
if(i==mid) b[num++]=a[j++];
else if(j==r) b[num++]=a[i++];
else if(a[i].y<a[j].y) b[num++]=a[i++];
else b[num++]=a[j++];
}
for(int i=l;i<=r;i++) a[i]=b[i];
for(int i=mid-1;i>=l;i--)
if(a[mid].x-a[i].x<res)
t[++cnt]=a[i];
for(int i=mid;i<r;i++)
if(a[i].x-a[mid].x<res)
t[++cnt]=a[i];
for(int i=1;i<=cnt;i++)
for(int j=i+1;j<=cnt&&t[j].y-t[i].y<res;j++) {
if(t[i].f==t[j].f) continue;
res=min(res,sqrt((t[i].x-t[j].x)*(t[i].x-t[j].x)+(t[i].y-t[j].y)*(t[i].y-t[j].y)));
}
return res;
}
signed main() {
T=read();
while(T--) {
n=read();
for(int i=1;i<=n;i++