1 问题描述
给定平面S上n个点,找其中的一对点,使得在 n(n − 1)/2个点对中, 该点对的距离最小。
输入描述
第一行为整数n,表示平面上有n个点,至少存在2个点
接下来n行,每行两个数,用空格隔开,表示x[i],y[i]
n不超过1000000,x,y的绝对值不超过10^5,且为整数
没有坐标完全相同的点
输出描述
一行输出,表示最近点对的距离的平方
输入为整数,所以结果也为固定的整数
测试输入 | 期待的输出 | 时间限制 | 内存限制 | 额外进程 | |
---|---|---|---|---|---|
测试用例 1 | 以文本方式显示
| 以文本方式显示
| 1秒 | 64M | 0 |
测试用例 2 | 以文本方式显示
| 以文本方式显示
| 1秒 | 64M | 0 |
测试用例 9 | 以文本方式显示
| 以文本方式显示
| 1秒 | 64M | 0 |
2解题
(1)蛮力法
-
之前写过同样的题目,直接查找所有的可能,时间复杂度 [ O ( n 2 ) ] [O({n^2})] [O(n2)]
-
在n个点中,两点分别是a(x1,y1),b(x2,y2),则两点距离是
d=sqrt((x1-x2)^2+(y1-y2)^2)
将平面内的n个点,两两组队,计算最小距离 -
直接计算平方就行。
-
代码
#include <cstdio>
#include <iostream>
#include<cmath>
using namespace std;
const double INF = 1e20;
typedef struct
{
double x;
double y;
} Point;
double spow2(double a) {
return a * a;
}
int main(int argc, char* argv[]) {
freopen("file in.txt", "r", stdin);
int n;
scanf("%d",&n);
double min = INF;
Point* p = NULL;
p = new Point[n];
for(int i = 0; i < n; i++)
scanf("%lf %lf", &p[i].x, &p[i].y);
for(int i = 0; i < n - 1; i++)
for(int j = i + 1; j < n; j++) {
double tmp = spow2(p[j].x - p[i].x) + spow2(p[j].y - p[i].y);
min = min > tmp ? tmp : min;
}
printf("%.2lf\n", min);
// printf("%.2lf\n", sqrt(min));
return 0;
}
- 很显然,当n很大的时候,时间复杂度很高,所以我的后面几个测试用例过不掉
(2)分治法
<1> 原理
- 对输入的点按x坐标升序排序
- 从中间n/2的地方分成两份,左边找出一个最小值min1,右边找出一个最小值min2,取更小的一个得到min(根据题意这里都是距离的平方),然后开方得到d;
- 在中间轴左右d的范围内把所有点存入新的数组;
-
- 按y轴升序排序
- 对于每一个点取出他之后的6个点(包括他),求出这6个点中的最小距离,和原来的min作比较,更小的话更新min的值,遍历每一个点
- 输出min
<2> 代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
struct node{
long long int x;
long long int y;
};
typedef struct node NODE;
long long int numpower(long long int a,long long int b){
long long int res=1;
for(long long int i=0;i<b;i++){
res *=a;
}
return res;
}
// 按照x坐标排序
void quicksortx(NODE a[],long long int begin,long long int end){
NODE temp;
long long int valuetemp;
long long int i,j;
i=begin;j=end;
if(begin<end){
temp = a[end];
valuetemp = a[end].x;
while(i<j){
while(i<j&&a[i].x<=valuetemp){
i++;
}
a[j] = a[i];
while(i<j&&a[j].x>=valuetemp){
j--;
}
a[i]=a[j];
}
a[i]=temp;
quicksortx(a,begin,i-1);
quicksortx(a,i+1,end);
}
else
return;
}
// 按照y坐标排序
void quicksorty(NODE a[],long long int begin,long long int end){
NODE temp;
long long int valuetemp;
long long int i,j;
i=begin;j=end;
if(begin<end){
temp = a[end];
valuetemp = a[end].y;
while(i<j){
while(i<j&&a[i].y<=valuetemp){
i++;
}
a[j] = a[i];
while(i<j&&a[j].y>=valuetemp){
j--;
}
a[i]=a[j];
}
a[i]=temp;
quicksorty(a,begin,i-1);
quicksorty(a,i+1,end);
}
else
return;
}
// 求出最小值
long long int getmin(NODE a[],long long int begin,long long int end){
long long int temp;
long long int min=999999999999;
long long int i,j;
for(i=begin;i<=end-1;i++){
for(j=i+1;j<=end;j++){
temp = numpower((a[j].x-a[i].x),2)+numpower((a[j].y-a[i].y),2);
if(min>temp)
min =temp;
}
}
return min;
}
int main(int argc,char *argv[]){
// freopen("file in.txt","r",stdin);
long long int n;
NODE *axis;
NODE *s;
long long int nums=0;
long long int i;
long long int min;
long long int min1,min2;
long long int d;
cin>>n;
axis = (NODE*)malloc(sizeof(NODE)*n);
// 把点存入结构数组中
for(i=0;i<n;i++){
cin>>axis[i].x>>axis[i].y;
}
quicksortx(axis,0,n-1); //按照x坐标排序
min1=getmin(axis,0,n/2); //得到坐标最小距离平方
min2=getmin(axis,n/2+1,n-1); //得到右边部分最小距离平方
min = min1>min2?min2:min1;
d=(long long int)sqrt(min)+1;
// 把中间范围内的点存到新的数组里面
long long int assist1 = axis[n/2].x-d;
long long int assist2 = axis[n/2].x+d;
s = (NODE*)malloc(sizeof(NODE)*n);
for(i=0;i<n;i++){
if(axis[i].x>=assist1||axis[i].x<=assist2){
s[nums].x = axis[i].x;
s[nums].y = axis[i].y;
nums++;
}
}
quicksorty(s,0,nums-1); //按照y坐标排序
// 遍历每一个点,每个点取出之后的5个值进行最小距离操作
for(i=0;i<nums-5;i++){
min1 =getmin(s,i,i+5);
min = min>min1?min1:min;
}
cout<<min<<endl;
return 0;
}
好吧,我还是没过!!!
- 上面这个程序的操作是在S1中求出d1,S2中求出d2,然后d=min(d1,d2), 之后在虚线范围内的每一个点都取出来进行至多6个点的操作,求出的距离和d进行比较
- 无法AC的原因,当n很大的时候,n/2也还是很大,( [ n 2 ] [{n^2}] [n2] --> [ n 2 / 4 ] [{n^2/4}] [n2/4]), 但是这一次缩减成为了1/4,那么我们分别对左边和右边进行相同的操作,由于是一模一样的,所以我们可以使用递归
<3>改进
- 定义数组的时候最初定义的名字为index[],结果报错了
说明index原来是一个内建函数,查了一下 - 头文件:#include <string.h>
定义函数:char * index(const char *s, int c);
函数说明:index()用来找出参数s 字符串中第一个出现的参数c 地址,然后将该字符出现的地址返回。字符串结束字符(NULL)也视为字符串一部分。
返回值:如果找到指定的字符则返回该字符所在地址,否则返回0.
示例:
#include <string.h>
main(){
char *s = "0123456789012345678901234567890";
char *p;
p = index(s, '5');
printf("%s\n", p);
}
-修改了一下数组名就行了
- 回归正题
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<time.h>
#include<iomanip>
using namespace std;
const double MAX=10000000000000;
struct node{
double x;
double y;
};
typedef struct node NODE;
NODE *axis; //存储各对点的结构数组,因为递归中要使用,避免每次都要当做参数传递,声明为全局变量
int *axisindex; //保存下标的数组
// double numpower(double a){
// return a*a;
// }
// 按照x坐标排序
int comparex(const node& a,const node& b){
return a.x<b.x; //升序
}
// 按照y坐标排序
//这里传进来的参数是下标,和上面的不一样,上面的结构
int comparey(int a,int b){
return axis[a].y<axis[b].y; //升序
}
//得到更小的值
double smaller(double a,double b){
return a<b?a:b;
}
//求出距离值的平方
// double distance(const node& a,const node& b){
// return numpower(a.x-b.x)+numpower(a.y-b.y);
// }
double distance(const node& a,const node& b){
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
double getmin(int begin,int end){
double min;
if(begin==end){//只有一个点,无法计算距离
return MAX;
}
if(begin+1==end){ //两个点,返回这两个点之间的距离的平方,这也是递归到最后期待的结果
return distance(axis[begin],axis[end]);
}
if(begin+2==end){ //三个点,如果不单独处理的话下一次递归分治的时候就会出现落单,那个单数无法计算
// 可能会漏掉更小的距离
min = smaller(distance(axis[begin],axis[begin+1]),distance(axis[begin+1],axis[end]));
min = smaller(distance(axis[begin],axis[end]),min);
return min;
}
//每一个分区还多余3个的情况
int mid = (begin+end)>>1; //右移一位,相当于/2,速度更快;
min = smaller(getmin(begin,mid),getmin(mid,end));//中间是连起来的的,只是分成了更小的规模
//运行到这一步说明递归结束了,已经找到了两边最小的距离
return min;
}
//处理中间部分
double addition(double min,int begin,int end){
int i,j,cnt=0;
// double d=sqrt(min);
double d=sqrt(min)+1; //加1是为了保险,开方的时候四舍五入
double edgeleft,edgeright;
int mid;
mid = (begin+end)>>1;
edgeleft = axis[mid].x-d;
edgeright = axis[mid].x+d;
//虚线范围里面的点都放在一起来处理,不区分左边和右边
for(i=0;i<=end;i++){
if(axis[i].x>=edgeleft&&axis[i].x<=edgeright){
axisindex[cnt++]=i;//不需要建立结构数组来存储这些位于虚线内部的点,只需要存他们在axis里面的下标就行
}
}
// 现在cnt的值刚好是该区域元素的个数
sort(axisindex,axisindex+cnt,comparey); //按y值升序
for(i=0;i<cnt;i++){
int k=i+7>cnt?cnt:i+7;//到了最后末尾的时候的处理
for(j=i+1;j<k;j++){//小于号,所以取了7
if(axis[axisindex[j]].y-axis[axisindex[i]].y>d)
break; // 最多的情况下是有6个,但是不一定有6个,如果第j个单是y坐标的差值
//就已经大于d了,那么后面的绝对不会比d小,直接舍弃,剪枝的道理
min = smaller(distance(axis[axisindex[i]],axis[axisindex[j]]),min);
//求出这两个点之间的距离并且比较后是否更新min的值
}
}
return min;
}
int main(int argc,char *argv[]){
// clock_t start,end;
// int totaltime;
// start = clock();
//freopen("file in.txt","r",stdin);
int n;
int i;
double min;
cin>>n;
axis = (NODE*)malloc(sizeof(NODE)*n);
//肯定不需要n个,但是不知道具体个数,直接申请最大的空间
axisindex = (int*)malloc(sizeof(int)*n);
// 把点存入结构数组中
for(i=0;i<n;i++){
cin>>axis[i].x>>axis[i].y;
}
sort(axis,axis+n,comparex);
min = getmin(0,n-1);
min = addition(min,0,n-1);
cout<<fixed<<setprecision(0)<<min<<endl;
// end = clock();
// totaltime = (int)(end-start)/CLOCKS_PER_SEC;
// cout<<totaltime<<endl;
return 0;
}
怎么回事,连结果都不对了吗?
- 最后请教了学长才知道原来是我对递归的理解出现了问题,认为只有最外面的那个(将就理解)才有虚线部分,所以我对s1和s2分别进行了递归以为就能求出两边各自最小距离
我想到了中间可能会分开
于是我在递归时右边使用了mid而不是mid+1,就是下面这样
我以为这样就能够解决问题
实际上忽略了递归结束的时候真实情况,分到最后还是要么2个要么3个的,而这个时候是分开求解的
就像下面这样
最小距离可能是在他们中间
所以每一个递归都要包括对中间虚线部分的处理 - 而且我对中间部分没有分左边和右边,而是放在同一个数组里面对每一个点都取出至多6个点来比较,这样子的话虽然省事,当然也不会影响最后的正确结果,但是其中就会重复计算一些之前已经计算了的点之间的距离,增加时间复杂度
所以还是应该改进一下,对左边部分的任意一个点,在右边部分取出至多6个点来进行操作
<4> 最终版
代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<time.h>
#include<iomanip>
using namespace std;
const double MAX=10000000000000;
struct node{
double x;
double y;
};
typedef struct node NODE;
NODE *axis; //存储各对点的结构数组,因为递归中要使用,避免每次都要当做参数传递,声明为全局变量
int *axisindex; //保存下标的数组
// double numpower(double a){
// return a*a;
// }
// 按照x坐标排序
int comparex(const node& a,const node& b){
return a.x<b.x; //升序
}
// 按照y坐标排序
//这里传进来的参数是下标,和上面的不一样,上面的结构
int comparey(int a,int b){
return axis[a].y<axis[b].y; //升序
}
//得到更小的值
inline double smaller(double a,double b){
return a<b?a:b;
}
//求出距离值的平方
// double distance(const node& a,const node& b){
// return numpower(a.x-b.x)+numpower(a.y-b.y);
// }
inline double distance(const node& a,const node& b){
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
// //处理中间部分 不区分左右,合为同一个
double addition(double min,int begin,int end){
int i,j,cnt=0;
double d=sqrt(min);
//double d=sqrt(min)+1; //加1是为了保险,开方的时候四舍五入
double edgeleft,edgeright;
int mid;
mid = (begin+end)>>1;
edgeleft = axis[mid].x-d;
edgeright = axis[mid].x+d;
//虚线范围里面的点都放在一起来处理,不区分左边和右边
for(i=begin;i<=end;i++){
if(axis[i].x>=edgeleft&&axis[i].x<=edgeright){
axisindex[cnt++]=i;//不需要建立结构数组来存储这些位于虚线内部的点,只需要存他们在axis里面的下标就行
}
}
// 现在cnt的值刚好是该区域元素的个数
sort(axisindex,axisindex+cnt,comparey); //按y值升序
for(i=0;i<cnt;i++){
int k=i+7>cnt?cnt:i+7;//到了最后末尾的时候的处理
for(j=i+1;j<k;j++){//小于号,所以取了7
if(axis[axisindex[j]].y-axis[axisindex[i]].y>d)
break; // 最多的情况下是有6个,但是不一定有6个,如果第j个单是y坐标的差值
//就已经大于d了,那么后面的绝对不会比d小,直接舍弃,剪枝的道理
min = smaller(distance(axis[axisindex[i]],axis[axisindex[j]]),min);
//求出这两个点之间的距离并且比较后是否更新min的值
}
}
return min;
}
double getmin(int begin,int end){
double min;
if(begin==end){//只有一个点,无法计算距离
return MAX;
}
if(begin+1==end){ //两个点,返回这两个点之间的距离的平方,这也是递归到最后期待的结果
return distance(axis[begin],axis[end]);
}
if(begin+2==end){ //三个点,如果不单独处理的话下一次递归分治的时候就会出现落单,那个单数无法计算
// 可能会漏掉更小的距离
min = smaller(distance(axis[begin],axis[begin+1]),distance(axis[begin+1],axis[end]));
min = smaller(distance(axis[begin],axis[end]),min);
return min;
}
//每一个分区还多余3个的情况
int mid = (begin+end)>>1; //右移一位,相当于/2,速度更快;
min = smaller(getmin(begin,mid),getmin(mid+1,end));//中间是连起来的的,只是分成了更小的规模
//运行到这一步说明已经找到了两边最小的距离
min = addition(min,begin,end);
return min;
}
int main(int argc,char *argv[]){
// clock_t start,end;
// int totaltime;
// start = clock();
// freopen("file in.txt","r",stdin);
int n;
int i;
double min;
cin>>n;
axis = (NODE*)malloc(sizeof(NODE)*n);
//肯定不需要n个,但是不知道具体个数,直接申请最大的空间
axisindex = (int*)malloc(sizeof(int)*n);
// leftindex = (int*)malloc(sizeof(int)*n);
// rightindex =(int*)malloc(sizeof(int)*n);
// 把点存入结构数组中
for(i=0;i<n;i++){
cin>>axis[i].x>>axis[i].y;
}
sort(axis,axis+n,comparex);
min = getmin(0,n-1);
min = addition(min,0,n-1);
cout<<fixed<<setprecision(0)<<min<<endl;
// end = clock();
// totaltime = (int)(end-start)/CLOCKS_PER_SEC;
// cout<<totaltime<<endl;
return 0;
}
- 这个并没有对虚线中的部分进行分开处理,而是全放在一起,然后对于里面的每一个点,每次最多取6个来求距离
<5> 小结
- 如果数组申请了地址后没有任何值赋给他,相当于这是一个空数组,这是如果用sort函数对其进行排序就会报错(语法错误!!)
### <6> 留坑 - 其实分成两部分的那个我也写了,但是有个案例(看不见的)居然结果错误,也是打了半天代码,先留着在这里吧,有时间了再回来看看 - 基本思想
![](https://i-blog.csdnimg.cn/blog_migrate/88a529b32284feef0c7b5aa469d4a0d5.png)
- 代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<time.h>
#include<iomanip>
using namespace std;
const double MAX=10000000000000;
struct node{
double x;
double y;
};
typedef struct node NODE;
NODE *axis; //存储各对点的结构数组,因为递归中要使用,避免每次都要当做参数传递,声明为全局变量
int *leftindex; //保存左边部分的下标
int *rightindex; //保存右边部分的下标
// 按照x坐标排序
int comparex(const node& a,const node& b){
return a.x<b.x; //升序
}
// 按照y坐标排序
//这里传进来的参数是下标,和上面的不一样,上面的结构
int comparey(int a,int b){
return axis[a].y<axis[b].y; //升序
}
//得到更小的值
inline double smaller(double a,double b){
return a<b?a:b;
}
//求出距离值的平方
inline double distance(const node& a,const node& b){
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
//处理中间部分 分开为左右两边
double addition(double min,int begin,int end){
int i,j,cnt1=0,cnt2=0;
double d=sqrt(min);
// double d=sqrt(min)+1; //加1是为了保险,开方的时候四舍五入
double edgeleft,edgeright;
int mid;
mid = (begin+end)>>1;
edgeleft = axis[mid].x-d;
edgeright = axis[mid].x+d;
int empty1=1,empty2=1;
//虚线范围里面的点都放在一起来处理,不区分左边和右边
for(i=begin;i<=end;i++){
if(axis[i].x>=edgeleft&&axis[i].x<=axis[mid].x){ //左边的
leftindex[cnt1++]=i;
empty1 =0; //有点存进来了,不是空的了
}
if(axis[i].x>axis[mid].x&&axis[i].x<=edgeright){ //右边的
rightindex[cnt2++]=i;
empty2=0;
}
}
if(empty1||empty2) //只要有一个为空,说明点全在某一边,后面的操作属于重复操作
return min; //而且sort函数操作一个没有任何赋值的已经申请空间的数组排序会报错
// 现在cnt的值刚好是该区域元素的个数
sort(leftindex,leftindex+cnt1,comparey); //按y值升序
sort(rightindex,rightindex+cnt2,comparey); //按y值升序
double dx,dy;
int assistindex=0;//让每一次遍历右边不需要从头开始
int firstenterflag; //遇到了第一个进入指定范围内的点,因为有顺序,所以之后的符合范围的都在它上面
for(i=0;i<cnt1;i++){ //遍历左边的每一个点
firstenterflag=1;
for(j=assistindex;j<cnt2;j++){
dx = axis[rightindex[j]].x-axis[leftindex[i]].x; //一定是非负数,因为是右边减去左边
dy = axis[rightindex[j]].y-axis[leftindex[i]].y; //可正可负
//第一个到达边界的点,记住他的下标,下一次从他开始,不用从头开始
if(dx>d||dy<-d)
continue; // 如果x方向上就大于d了,那么距离肯定不会小于d,对于最后更新min没有用,跳过
if(dy>=d)
break; // 再往上就没必要比较了
if(firstenterflag){
assistindex = j;
firstenterflag=0;
}
min = smaller(distance(axis[rightindex[j]],axis[leftindex[i]]),min);
//求出这两个点之间的距离并且比较后是否更新min的值
}
}
return min;
}
double getmin(int begin,int end){
double min;
if(begin==end){//只有一个点,无法计算距离
return MAX;
}
if(begin+1==end){ //两个点,返回这两个点之间的距离的平方,这也是递归到最后期待的结果
return distance(axis[begin],axis[end]);
}
if(begin+2==end){ //三个点,如果不单独处理的话下一次递归分治的时候就会出现落单,那个单数无法计算
// 可能会漏掉更小的距离
min = smaller(distance(axis[begin],axis[begin+1]),distance(axis[begin+1],axis[end]));
min = smaller(distance(axis[begin],axis[end]),min);
return min;
}
int mid = (begin+end)>>1; //右移一位,相当于/2,速度更快;
min = smaller(getmin(begin,mid),getmin(mid,end));
//运行到这一步说明递归结束了,已经找到了两边最小的距离
min = addition(min,begin,end);
return min;
}
int main(int argc,char *argv[]){
// clock_t start,end;
// int totaltime;
// start = clock();
// freopen("file in.txt","r",stdin);
int n;
int i;
double min;
cin>>n;
axis = (NODE*)malloc(sizeof(NODE)*n);
//肯定不需要n个,但是不知道具体个数,直接申请最大的空间
leftindex = (int*)malloc(sizeof(int)*n);
rightindex =(int*)malloc(sizeof(int)*n);
// 把点存入结构数组中
for(i=0;i<n;i++){
cin>>axis[i].x>>axis[i].y;
}
sort(axis,axis+n,comparex);
min = getmin(0,n-1);
min = addition(min,0,n-1);
cout<<fixed<<setprecision(0)<<min<<endl;
// end = clock();
// totaltime = (int)(end-start)/CLOCKS_PER_SEC;
// cout<<totaltime<<endl;
return 0;
}
<填坑>
先贴一下数据结构中讲到的,我进行了实现
效果并不好,这里主要是想把排序过程放在分治的外面
/******************************************
* @Author : 鱼香肉丝没有鱼
* @Date : 2021-09-25 16:04:09
* @LastEditors : 鱼香肉丝没有鱼
* @LastEditTime : 2021-11-18 21:06:46
******************************************/
// 最近点对程序
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <stdbool.h>
using namespace std;
#define N 2000000
const double INF = 1e18;
struct PointA
{
int id; //点的编号
double x;
double y;
};
struct PointA a[N];
struct PointB
{
int p; //同一点在数组x中的编号
double x;
double y;
};
template <class type>
void Swap(type& a, type& b) {
type tmp = a;
a = b;
b = tmp;
}
// 计算两个点之间的距离
template <class Type>
inline double distance(const Type& u, const Type& v) {
double dx = u.x - v.x;
double dy = u.y - v.y;
return sqrt(dx * dx + dy * dy);
}
//归并排序X
void MergeSortX(int begin, int end) {
if(begin >= end)
return;
int idx = rand() % (end - begin + 1) + begin;
Swap(a[begin], a[idx]);
PointA pivot = a[begin];
int i = begin, j = end;
while(i < j) {
while(i < j && a[j].x >= pivot.x)
j--;
a[i] = a[j];
while(i < j && a[i].x <= pivot.x)
i++;
a[j] = a[i];
}
a[i] = pivot;
MergeSortX(begin, i - 1);
MergeSortX(i + 1, end);
}
//归并排序Y
void MergeSortY(PointB* b, int begin, int end) {
if(begin >= end)
return;
int idx = rand() % (end - begin + 1) + begin;
Swap(b[begin], b[idx]);
PointB pivot = b[begin];
int i = begin, j = end;
while(i < j) {
while(i < j && b[j].y >= pivot.y)
j--;
b[i] = b[j];
while(i < j && b[i].y <= pivot.y)
i++;
b[j] = b[i];
}
b[i] = pivot;
MergeSortY(b, begin, i - 1);
MergeSortY(b, i + 1, end);
}
//直接计算
void directCalcul(PointB c[], int left, int right, PointA& pa, PointA& pb, double& d) {
if(left == right) {
d = INF;
return;
}
//两个点
double d1 = distance(c[left], c[right]);
d = d1;
pa = a[c[left].p];
pb = a[c[right].p];
if(left + 2 == right) { //三个点
int i = left + 1;
double d2 = distance(c[left], c[i]);
double d3 = distance(c[i], c[right]);
if(d > d2) {
d = d2;
pa = a[c[left].p];
pb = a[c[i].p];
}
if(d > d3) {
d = d3;
pa = a[c[i].p];
pb = a[c[right].p];
}
}
}
//合并
void Merge(PointB b[], PointB c[], int left, int mid, int right) {
int i, j, k = left;
i = left;
j = mid + 1;
while(i <= mid && j <= right) {
if(c[i].y < c[j].y)
b[k++] = c[i++];
else
b[k++] = c[j++];
}
while(i <= mid)
b[k++] = c[i++];
while(j <= right)
b[k++] = c[j++];
}
//在[l:r]中取最近点对
void closest(PointB b[], PointB c[], int left, int right, PointA& pa, PointA& pb, double& d) {
if(right - left <= 2) { // 2点或者3的问题,直接计算
directCalcul(b, left, right, pa, pb, d);
return;
}
//多于三个点的情况进行分治,Y分到Z中
int mid = (left + right) >> 1; //从中间进行份分割
int f = left;
int g = mid + 1;
for(int i = left; i <= right; i++) { // Y中的数据是按照纵坐标进行排序的
if(b[i].p > mid)
c[g++] = b[i];
else
c[f++] = b[i];
}
closest(c, b, left, mid, pa, pb, d); //治:左边left:mid;
double dr;
PointA ar, br;
closest(c, b, mid + 1, right, ar, br, dr); //右边
if(dr < d) { //合,d
pa = ar;
pb = br;
d = dr;
}
Merge(b, c, left, mid, right); //把c合并到b中
int k = left;
for(int i = left; i <= right; i++) {
// if(fabs(b[mid].x - b[i].x) < d)
// c[k++] = b[i];
if(fabs(a[mid].x - b[i].x) < d) //
c[k++] = b[i];
}
for(int i = 0; i < k; i++)
for(int j = i + 1; j < k && c[j].y - c[i].y < d; j++) {
double dp = distance(c[i], c[j]);
if(dp < d) {
d = dp;
pa = a[c[i].p];
pb = a[c[j].p];
}
}
}
bool Cpair2(int n, PointA& pa, PointA& pb, double& d) {
// if(n<2)
// return false;//少于三个点返回false
MergeSortX(0, n - 1); // x按横坐标进行排序
struct PointB* b = NULL;
b = new PointB[n];
for(register int i = 0; i < n; i++) {
b[i].p = i; //这是排过一次序的位置
b[i].x = a[i].x;
b[i].y = a[i].y;
}
MergeSortY(b, 0, n - 1);
struct PointB* c = NULL;
c = new PointB[n];
closest(b, c, 0, n - 1, pa, pb, d);
delete[] b;
delete[] c;
return true;
}
int main(int argc, char* argv[]) {
// freopen("test data.in", "r", stdin);
// freopen("file in.txt", "r", stdin);
freopen("file.txt", "r", stdin);
int n;
scanf("%d", &n);
srand(time(NULL));
double xx, yy;
for(int i = 0; i < n; i++) {
scanf("%lf %lf", &xx, &yy);
a[i].id = i;
a[i].x = xx;
a[i].y = yy;
}
PointA pa;
PointA pb;
double d;
Cpair2(n, pa, pb, d);
// printf("%lf %lf \n%lf %lf\n %.2f\n", pa.x, pa.y, pb.x, pb.y, d);
printf("%.2lf\n", d / 2);
return 0;
}
最后贴一个我在网上看到的,运行速度不知道为什么,特别快
这个程序还让我学会了引用的另一种特点
/******************************************
* @Author : 鱼香肉丝没有鱼
* @Date : 2021-09-20 12:55:54
* @LastEditors : 鱼香肉丝没有鱼
* @LastEditTime : 2021-12-10 09:44:18
******************************************/
// https://www.zsdocx.com/p-3399159.html
//排序放到分治外面
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
# include<ctime>
using namespace std;
#define N 2000000
const double inf = 5e18;
typedef struct
{
double x;
double y;
} Point;
int cmpx(const void* a, const void* b) {
return ((Point*)a)->x - ((Point*)b)->x;
}
int cmpy(const void* a, const void* b) {
return ((Point*)a)->y - ((Point*)b)->y;
}
double spow(const double& a) {
return a * a;
}
double dist(Point a, Point b) {
return sqrt(spow(a.x - b.x) + spow(a.y - b.y));
}
double Nearest(Point* p, int n) {
double d1, d2, d3, dd, min_d;
double left_d, right_d, d;
int i, j;
if(n == 1)
return inf;
if(n == 2)
return dist(p[0], p[1]);
if(n == 3) {
d1 = dist(p[0], p[1]);
d2 = dist(p[1], p[2]);
d3 = dist(p[2], p[3]);
return d3 < ((d1 < d2) ? d1 : d2) ? d3 : ((d1 < d2) ? d1 : d2);
}
left_d = Nearest(p, n / 2);
right_d = Nearest(&p[n / 2], n - n / 2);
d = left_d < right_d ? left_d : right_d;
min_d = d;
for(i = 0; i < n / 2; i++)//左边部分
for(j = n / 2; j < n && fabs(p[j].y - p[i].y) < d; j++) {//右边部分
dd = dist(p[i], p[j]);
min_d = min_d < dd ? min_d : dd;
}
return min_d;
}
int main() {
clock_t start,end;
double total;
start = clock();
int i;
int n = 0;
double dis = 0;
Point* p = NULL;
// freopen("test data.in", "r", stdin);
freopen("file in.txt", "r", stdin);
// freopen("file.txt", "r", stdin);
scanf("%d", &n);
if(!n)
return 0;
p = new Point[n];
for(i = 0; i < n; i++) {
scanf("%lf %lf", &p[i].x, &p[i].y);
}
qsort(p, n, sizeof(Point), cmpx);
qsort(p, n / 2, sizeof(Point), cmpy);
qsort(p + n / 2, n - n / 2, sizeof(Point), cmpy);
dis = Nearest(p, n);
printf("%.2lf\n", dis);
end = clock();
total = (end-start)/CLOCKS_PER_SEC;
cout<<total<<endl;
return 0;
}