袭击
这道题目的解法是在计算距离时,如果是同一类点就认为距离时无穷大
但是这样的话就会存在一个问题,如果我们递归到某一层的时候,得到的最小距离还是无穷大,那么此时我们再执行原来的算法,时间复杂度就会变成\(O(n^2)\)
即使我们使用一般的算法(就是普通的平面最近点对),然后把
for(int i=1;i<cnt;i++)
for(int j=i+1;j<=cnt&&(temp[j].y-temp[i].y)*(temp[j].y-temp[i].y)<d;j++)//最多只有6个点
d=min(d,dis(temp[i],temp[j]));
改成
for(int i=1;i<cnt;i++)
for(int j=i+1;j<=cnt&&temp[j].y-temp[i].y<d;j++)
if(temp[i].id^temp[j].id==1) d=min(d,dis(temp[i],temp[j]));
也不行
首先一种hack数据就是同一个地方可能有多个重合的点,此时我们有一个很好的解决方法,对同一类型的点,在相同位置的只保留一个就行了,这里用hash即可
另一种hack数据就是最开始提到的情况,比如这道题目的最后一个数据,所有点都分布在\(y\)轴上,然后前半部分(按照纵坐标排序)全部是第一类点,后半部分全部是第二类点,这样的话时间复杂度就变成了\(O(n^2)\);本来想到的一种解决方法是判断递归树上某一点是否只有一种点,如果是这种情况直接返回,但是这种处理方法也存在一种问题,如果这一点的左半部分和右半部分只有一种点,但是两个部分的点的种类不同,此时你会发现没办法做了;题解区有一个随机化的算法,可以学一下,确定性算法目前还没有想到
确定性的大致代码见下
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+10;
map<pair<ll,ll>,bool> h;
int n;
struct Node
{
ll x,y;
int id;
}temp[N],w[N],s1[N],s2[N];
int read()
{
int x=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-f;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-48;s=getchar();}
return x*f;
}
bool cmp(Node i,Node j)
{
if(i.x==j.x) return i.y<j.y;
return i.x<j.x;
}
double dis(Node i,Node j)
{
return sqrt((i.x-j.x)*(i.x-j.x)+(i.y-j.y)*(i.y-j.y));
}
double cal(int l,int r)
{
if(l==r) return 10000000000;
bool flag=0;
for(int i=l+1;i<=r;i++)
if(w[i].id!=w[l].id)
{
flag=1;
break;
}
if(!flag) return 10000000000;
int mid=l+r>>1,cnt=0,tot=0;
double d1=cal(l,mid),d2=cal(mid+1,r);
if(d1==10000000000&&d2==10000000000)
{
}
double d=min(d1,d2);
int i=l,j=mid+1;
while(i<=mid||j<=r)
{
while(i<=mid&&abs(w[i].x-w[mid].x)>=d)//先筛选出在虚线框中的点
i++;
while(j<=r&&abs(w[j].x-w[mid].x)>=d)
j++;
if(i<=mid)
{
if(j<=r)
{
if(w[i].y<w[j].y) temp[++cnt]=w[i++];//仍然按照y坐标升序
else temp[++cnt]=w[j++];
}
else temp[++cnt]=w[i++];
}
else if(j<=r) temp[++cnt]=w[j++];
}
for(int i=1;i<cnt;i++)
for(int j=i+1;j<=cnt&&temp[j].y-temp[i].y<d;j++)
if(temp[i].id^temp[j].id==1) d=min(d,dis(temp[i],temp[j]));
i=l,j=mid+1;
int k=l;
while(i<=mid&&j<=r)//归并排序
{
if(w[i].y<w[j].y) temp[k++]=w[i++];
else temp[k++]=w[j++];
}
while(i<=mid) temp[k++]=w[i++];
while(j<=r) temp[k++]=w[j++];
for(int i=l;i<=r;i++)
w[i]=temp[i];
return d;
}
int main()
{
int t=read();
while(t--)
{
n=read();
int cnt=0;
for(int i=1;i<=n;i++)
{
ll x=read(),y=read();
if(h.find(make_pair(x,y))!=h.end()) continue;
h[make_pair(x,y)]=1;
w[++cnt].x=x,w[cnt].y=y,w[cnt].id=0;
}
h.clear();
for(int i=1;i<=n;i++)
{
ll x=read(),y=read();
if(h.find(make_pair(x,y))!=h.end()) continue;
h[make_pair(x,y)]=1;
w[++cnt].x=x,w[cnt].y=y,w[cnt].id=1;
}
sort(w+1,w+cnt+1,cmp);
printf("%.3lf\n",cal(1,cnt));
}
return 0;
}