题目描述
给定平面上 nn 个点,找出其中的一对点的距离,使得在这 nn 个点的所有点对中,该距离为所有点对中最小的。
输入格式
第一行一个整数 nn,表示点的个数。
接下来 nn 行,每行两个实数 x,yx,y ,表示一个点的行坐标和列坐标。
输出格式
仅一行,一个实数,表示最短距离,四舍五入保留 44 位小数。
输入输出样例
输入 #1复制
3 1 1 1 2 2 2
输出 #1复制
1.0000
思路
这道题本弱鸡参考了洛谷一位大佬的,模板和归并排序差不多
一些必要提一下的区别是
1.归并逆序对(多少组逆序对)的属性是sum(求和),sum也会用于归并过程代码中
归并结果表达式为
res=merge_sort(l,mid)+merge_sort(mid+1,r)
归并过程表达式为
res+=mid-i+1;
2.此题的属性是min,min也会由于归并过程代码中
归并结果表达式为
res=min(merge(l,mid),merge(mid+1,r));
归并过程表达式为
res=min(res,dis(temp[j],temp[i]));
******因为属性是min,res要初始化为无穷大,不然输出结果永远为0
/*
虽然可以分为两个点都在左边,或者两个点都在右边,或者一个点在左边一个点在右边
但我们还是要把 都在左边的和都在右边的一同视为跨界两点(也就是归于第三种),因为在递归的过程中,这算一个小规模的分界
*/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e4+10;
typedef long long LL;
struct node
{
double x,y;
}p[N];
int temp[N]; //临时数组
bool cmp(const node &a,const node &b)
{
if(a.x==b.x)
return a.y<b.y;
else
return a.x<b.x; //是不是多余
}
bool cmp2(const int &a,const int &b)
{
return p[a].y<p[b].y;
}
double dis(int i,int j)
{
return sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y));
}
double merge(int l,int r)
{
double res=0x3f3f3f3f; //*****因为涉及到属性为min,有比较,先把res设为无穷大,否则输出结果为0
if(l>=r)
return res; //不能return 0
/*if(l+1==r)
{
return dis(l,r);
}*/
int mid=l+r>>1;
//归并结果表达式 ,归并的属性在归并过程中也要体现
res=min(merge(l,mid),merge(mid+1,r));
//归并过程,求当前的最小值
int k=0;
for(int i=l;i<=r;i++)
{
/*如果想找到一对解,我们把res看成斜边r,
根据勾股定理,两直角边都要小于斜边,才
有可能构成直角三角形,那么在所有成双的直角边都小于r的情况中,
可以筛选出斜边比r小的一对直角边,这些斜边再比较,选出新的res
*/
if(fabs(p[mid].x-p[i].x)<res)
{
temp[k++]=i; //横向符合要求的点已经筛选出来,最终的成对的点都在temp数组里面找
}
sort(temp,temp+k,cmp2);
for(int i=0;i<k;i++)
{
for(int j=i+1;j<k;j++)
{
if(fabs(p[temp[j]].y-p[temp[i]].y)<res) //纵向也要符合要求
res=min(res,dis(temp[j],temp[i])); //结果的更新
}
}
}
return res;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
double x,y;
cin>>x>>y;
p[i].x=x;
p[i].y=y;
}
sort(p,p+n,cmp);
printf("%.4lf\n",merge(0,n-1));
}