平面最近点对(分治)

Description
给定 n 个平面上的点,求最近两个点的编号。

Input
每组数据第一行 2 ≤ n ≤ 10^5,接下来 n 行每行两个整数 0 ≤ xi, yi ≤ 10^4, i = 0, 1, 2, ⋯, n − 1,表示第i个点的坐标。

Output
输出最近两个点的编号 a 和 b,按 a < b 输出。如果有多个答案,输出 a 最小的,如果依然有多个答案,在这些答案里输出 b 最小的。

Sample Input
3
0 0
1 1
3 3
Sample Output
0 1

该题属于算法实验分治练习中的题目,还是利用分治的思想解决

首先,我们还是将问题分割成一个一个的规模较小的问题,这里容易想到,规模最小的问题就是当我们已经将所有点分割到只剩两个点了,那么此时最近两个点的就是这两个点了,再考虑正常情况下,我们肯定会将所有点分割成两边各自求问题(分治思想),那么最近的两个点可能会出现在哪里呢,这里也比较容易想到的是两个点都出现在分割后的左边、都出现在分割后的右边,一个在左一个在右,那么问题的答案很明显就是在这三种情况中取一种即可

那么前两种情况比较容易计算,按照上面的(将所有点分割到只剩两个点了)递归计算即可(将二者的较小值设为ans),主要考虑一个在左和一个在右,我们不可能遍历两边所有的点进行计算的,因此这里需要一些优化,这里读者可画图理解,我们取最中间的点作一条中垂线,然后我们通过ans来筛选出可能出现更小值的点进行计算,只有那些到中垂线距离小于ans的点才有可能出现更小距离,因为如果横向距离的都超过ans了那肯定不可能小于ans啦,同理对于y轴上也是如此在这里插入图片描述

 上图借鉴自

分析到这里我们就可以写代码了

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<climits>
using namespace std;
const int MAX=1e5+10;
const int INF = 2 << 20;

typedef struct Node{
	double x,y;
	int id;
}Node;

int n;
Node arr[MAX];
int temp[MAX];
int ans1=2147483647,ans2=2147483647;//序号 
double len=2147483647;//最小距离 

bool cmp(const Node &a, const Node &b) 
{
    if(a.x == b.x) return a.y < b.y;//按照x排序 
    else return a.x < b.x;
}

void updatee(int id1,int id2,double cur){
	if(id1>id2){//保证序号顺序 
		int temp=id1;
		id1=id2;
		id2=temp;
	}
	if(cur!=len){//更新序号和最小距离 
		ans1=id1;
		ans2=id2;
		len=cur;
	}else{
		if(id1<ans1||(id1==ans1&&id2<ans2)){//距离相等情况下按照序号更新 
			ans1=id1;
			ans2=id2;
		}
	}
}

double dis(int left,int right){
	double a=(arr[right].x-arr[left].x)*(arr[right].x-arr[left].x);
	double b=(arr[right].y-arr[left].y)*(arr[right].y-arr[left].y);
	return sqrt(a+b);
}

void fun(int left,int right){
	if(left==right){
		return ;
	}
	if(right-left==1){
		double cur=dis(left,right);
		if(cur<=len){//注意距离相等情况下也要进函数 
			updatee(arr[left].id,arr[right].id,cur);
		}
		return ;
	}
	int mid=(left+right)/2;
	fun(left,mid);//左区间 
	fun(mid+1,right);//右区间 
	//还有中间区间
	int k=0;
	for(int i=left;i<=right;i++){
		if(fabs(arr[i].x-arr[mid].x)<=len){
			temp[k++]=i;
		}
	} 
	for(int i=0;i<k;i++){
		for(int j=i+1;j<k;j++){
			double s=dis(temp[i],temp[j]);
			if(s<=len){//注意距离相等情况下也要进函数 
				updatee(arr[temp[i]].id,arr[temp[j]].id,s);
			}
		}
	}
}

int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%lf%lf",&arr[i].x,&arr[i].y);
		arr[i].id=i;
	}
	sort(arr,arr+n,cmp);//排序方便取出中间值
	fun(0,n-1);
//	printf("%.4lf\n",len); 
	printf("%d %d\n",ans1,ans2);
	return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
最近对问题是指在平面上给定N个,求其中距离最近的两个之间的距离。分治法是一种常用的解决最近对问题的算法分治法的基本思想是将问题分解成多个子问题,然后递归地解决这些子问题,最后将子问题的解合并起来得到原问题的解。对于最近对问题,我们可以采用以下的分治算法: 1. 将所有按照横坐标从小到大排序,然后将它们划分为两部分,分别处理左右两个部分。 2. 对于左右两个部分,分别递归求解最近对距离。 3. 将左右两个部分中距离最近对的距离记为d。 4. 扫描所有横坐标在中间区域内的,按照纵坐标从小到大排序。对于每个,只需考虑它与纵坐标差不超过d的之间的距离,因为这些是可能成为最近对的候选者。 5. 对于每个,只需要考虑它与后面7个之间的距离即可,因为如果有距离更近的,它们之间的距离一定小于d。 6. 扫描完中间区域内的所有后,最近对的距离就是左右两个部分中的最小值和中间区域内的最小值中的较小值。 下面是该算法的伪代码: ``` function closest_pair(points): // 按照横坐标排序 sort(points, key=lambda p: p.x) // 递归终止条件 if len(points) <= 3: return brute_force(points) // 求解左右两个部分的最近对距离 mid = len(points) // 2 left_points = points[:mid] right_points = points[mid:] left_min_dist = closest_pair(left_points) right_min_dist = closest_pair(right_points) // 求解中间区域的最近对距离 min_dist = min(left_min_dist, right_min_dist) strip_points = [p for p in points if abs(p.x - points[mid].x) < min_dist] strip_points.sort(key=lambda p: p.y) for i in range(len(strip_points)): for j in range(i+1, min(i+8, len(strip_points))): dist = distance(strip_points[i], strip_points[j]) min_dist = min(min_dist, dist) return min_dist ``` 其中,brute_force是暴力求解最近对距离的函数,distance是计算两个之间距离的函数。时间复杂度约为O(N log N)。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZZZWWWFFF_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值