17. 最近对问题

1 问题描述

给定平面S上n个点,找其中的一对点,使得在 n(n − 1)/2个点对中, 该点对的距离最小。

输入描述

第一行为整数n,表示平面上有n个点,至少存在2个点

接下来n行,每行两个数,用空格隔开,表示x[i],y[i]

 

n不超过1000000,x,y的绝对值不超过10^5,且为整数

没有坐标完全相同的点


输出描述

一行输出,表示最近点对的距离的平方

输入为整数,所以结果也为固定的整数


 测试输入 期待的输出 时间限制 内存限制 额外进程
测试用例 1以文本方式显示
  1. 5↵
  2. 1 1↵
  3. 1 9↵
  4. 9 1↵
  5. 9 9↵
  6. 0 10↵
以文本方式显示
  1. 2↵
1秒64M0
测试用例 2以文本方式显示
  1. 2↵
  2. 100000 100000↵
  3. -100000 -100000↵
以文本方式显示
  1. 80000000000↵
1秒64M0
测试用例 9以文本方式显示
  1. 12849↵
  2. 5322 12906↵
  3. 19747 22152↵
  4. 23718 9090↵
  5. 3234 12638↵
  6. 19232 64125↵
  7. 63675 16878↵
  8. 12998 16110↵
  9. 4320 43701↵
  10. 9864 13365↵
  11. 16041 67688↵
  12. 524 44940↵
  13. 57390 1728↵
  14. 8017 7452↵
  15. 4876 34895↵
  16. 48890 16816↵
  17. 35147 29953↵
  18. 67508 9072↵
  19. 15122 25110↵
  20. 11514 16810↵
  21. 7614 49567↵
  22. 842 2345↵
  23. 11529 19908↵
  24. 6646 1224↵
  25. 13168 6446↵
  26. 18396 11465↵
  27. 28602 51328↵
  28. 5436 1961↵
  29. 19755 58110↵
  30. 4815 21861↵
  31. 1690 17028↵
  32. 67096 91260↵
  33. 4338 13608↵
  34. 17128 83340↵
  35. 2772 23009↵
  36. 29510 35510↵
  37. 76914 20307↵
  38. 78168 46781↵
  39. 55500 12966↵
  40. 9863 4536↵
  41. 13488 17768↵
  42. 72018 64062↵
  43. 7577 1244↵
  44. 4900 5325↵
  45. 38325 21329↵
  46. 3688 4920↵
  47. 5080 22372↵
  48. 13725 66123↵
  49. 8572 35596↵
  50. 20416 34344↵
  51. 75128 4488↵
  52. 49690 14020↵
  53. 7517 11912↵
  54. 9955 15498↵
  55. 8150 1329↵
  56. 4265 12306↵
  57. 81774 86600↵
  58. 14560 28532↵
  59. 8296 11130↵
  60. 43974 33594↵
  61. 3423 33904↵
  62. 16760 19671↵
  63. 38754 8635↵
  64. 9374 77808↵
  65. 28602 18964↵
  66. 30790 9238↵
  67. 1152 26410↵
  68. 9353 9366↵
  69. 7767 41097↵
  70. 16845 35630↵
  71. 792 77730↵
  72. 59320 28308↵
  73. 27616 9486↵
  74. 43232 24525↵
  75. 24552 9028↵
  76. 97940 30891↵
  77. 924 54747↵
  78. 1449 53824↵
  79. 54384 1332↵
  80. 8035 7994↵
  81. 44568 32152↵
  82. 41574 21609↵
  83. 16899 47630↵
  84. 7026 9121↵
以文本方式显示
  1. 37↵
1秒64M0

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> 留坑 - 其实分成两部分的那个我也写了,但是有个案例(看不见的)居然结果错误,也是打了半天代码,先留着在这里吧,有时间了再回来看看 - 基本思想
  • 代码
#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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来解答这个问题。首先,根据提供的数据,我们需要对各地区进行 C 均值聚类分析。 C 均值聚类是一种无监督学习算法,可以将数据集划分为 K 个不同的类别。其基本思想是:先随机选择 K 个点作为初始聚类心,然后计算每个数据到这 K 个聚类心的距离,将每个数据划分到距离最近的聚类心所在的类别,最后重新计算每个类别的聚类心。这个过程不断迭代,直到聚类心不再改变或者达到预定的迭代次数。 在 MATLAB ,我们可以使用 `kmeans` 函数进行 C 均值聚类。具体用法如下: ```matlab % 将数据存储在矩阵 X X = [12959.19 730.79 749.41 513.34 467.87 1141.82 478.42 457.64; 22459.77 495.48 697.33 302.87 284.19 735.97 570.84 305.08; 31495.63 515.90 362.37 285.32 272.95 540.58 364.91 188.63; 41406.33 477.77 290.15 208.57 201.50 414.72 281.84 212.10; 51303.97 524.29 254.83 192.17 249.81 463.09 287.87 192.96; 61730.84 553.90 246.91 279.81 239.18 445.20 330.24 163.86; 71561.86 492.42 200.49 218.36 220.69 459.62 360.48 147.76; 81410.11 510.71 211.88 277.11 224.65 376.82 317.61 152.85; 93712.31 550.75 893.37 346.93 527.00 1034.98 720.33 462.03; 102207.58 449.37 572.40 211.92 302.09 585.23 429.77 252.54; 112629.17 557.32 689.73 435.69 514.66 795.88 575.76 323.36; 121844.78 430.29 271.28 126.33 250.56 513.18 314.00 151.39; 132709.46 428.11 334.12 160.77 405.14 461.67 535.13 232.29; 141563.78 303.65 233.81 107.90 209.70 393.99 509.39 160.12; 151675.76 613.32 550.71 219.79 272.59 599.43 371.62 211.84; 161427.65 431.79 288.55 208.14 217.00 337.76 421.31 165.32; 171783.43 511.88 282.84 201.01 237.60 617.74 523.52 182.52; 181942.23 512.27 401.39 206.06 321.29 697.22 492.60 226.45; 190169.06 335.21 279.84 173.81 182.73 372.39 399.05 132.89]; % 对数据进行 C 均值聚类分析,分为 3 类 [idx, C] = kmeans(X, 3); % 将聚类结果可视化 figure; scatter3(X(:,1), X(:,2), X(:,3), 20, idx, 'filled'); xlabel('x1'); ylabel('x2'); zlabel('x3'); title('C 均值聚类分析结果'); ``` 以上代码的解释如下: - 第一行将数据存储在矩阵 X 。 - 第四行对数据进行 C 均值聚类分析,将数据分为 3 类,其 idx 是每个数据所属的类别,C 是聚类心。 - 第七行将聚类结果可视化,使用不同的颜色表示不同的类别,其 x1、x2、x3 分别表示数据的三个维度。 执行以上代码后,可以得到 C 均值聚类分析的结果,如下图所示: ![C 均值聚类分析结果](https://img-blog.csdnimg.cn/20220218154330398.png)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值