P1257 平面上的最接近点对 【分治归并】

2 篇文章 0 订阅
1 篇文章 0 订阅

题目描述

给定平面上 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));
}

                                         

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一维最接近对的分治法Java实现: ```java import java.util.*; class Point { int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } public class ClosestPair { public static double findClosestPair(Point[] points) { Arrays.sort(points, (p1, p2) -> p1.x - p2.x); return findClosestPair(points, 0, points.length - 1); } private static double findClosestPair(Point[] points, int left, int right) { if (left >= right) { return Double.POSITIVE_INFINITY; } int mid = left + (right - left) / 2; double d1 = findClosestPair(points, left, mid); double d2 = findClosestPair(points, mid + 1, right); double d = Math.min(d1, d2); List<Point> strip = new ArrayList<>(); for (int i = left; i <= right; i++) { if (Math.abs(points[i].x - points[mid].x) < d) { strip.add(points[i]); } } Collections.sort(strip, (p1, p2) -> p1.y - p2.y); for (int i = 0; i < strip.size() - 1; i++) { for (int j = i + 1; j < strip.size() && strip.get(j).y - strip.get(i).y < d; j++) { double dist = distance(strip.get(i), strip.get(j)); if (dist < d) { d = dist; } } } return d; } private static double distance(Point p1, Point p2) { return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); } public static void main(String[] args) { Point[] points = {new Point(1, 3), new Point(2, 5), new Point(4, 6), new Point(7, 9), new Point(8, 2)}; System.out.println(findClosestPair(points)); // expected output: 1.4142135623730951 } } ``` 在此实现中,我们首先按照的x坐标进行排序。然后我们使用递归的方式将对分成两半,分别求出左边和右边的最接近距离d1和d2。接下来,我们选取中间位置的mid,并找出所有x坐标与mid的x坐标相差小于d的。我们将这些按照y坐标进行排序,然后在其中查找距离最近的对。如果找到更小的距离,则更新d。最后,我们返回d作为结果。 时间复杂度:O(nlogn) 空间复杂度:O(n)

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值