平面最近点对,分治

传输门P1429 平面最近点对(加强版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1429

题目描述

给定平面上 n 个点,找出其中的一对点的距离,使得在这 n 个点的所有点对中,该距离为所有点对中最小的

输入输出样例

输入 #1


3
1 1
1 2
2 2

输出 #1

1.0000

 一,暴力解法

#include<bits/stdc++.h>

using namespace std;

const int N = 1000;

struct node{
	double x, y;
}a[N];
#define INF 1<<31-1
int n;

double dis(node x, node y)
{
	return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}
int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++)
	{
		cin >> a[i].x >> a[i].y;
	}
	
	double minn = INF;
	for(int i = 1; i < n; i++)
	{
		for(int j = i + 1; j <= n; j++)
		{
			minn = min(minn, dis(a[i], a[j]));
		}
	}
	cout << minn;
}

二,分治法

1,首先我们先对各个点的x坐标进行排序

2,把各个点平分成两部分,我们求左边部分的点中的两点之间的最小距离d1,再求出右边部分的点中的两点之间的最小距离d2。比较这两个最小距离,求出最小的最小距离d。

3,我们应该考虑到另一种情况,即左边有一个点,右边有一个点,这两点之间的距离比左边右边的最小距离还要小,那么,我们如何找到这两个点呢。

 首先,我遍历所以的点,如果有个点和中间点的距离 <= minn,那我们就把这个点的下标记录下来。

其次,我们对这记录下来的几个点进行遍历,寻找最小距离的两个点。这时候你可能就会有疑问了,这不还是两层循环吗,但是实际上,如果我们已经确定了一个点,那另一个点就只有六种情况。

可以看出,内层循环其实是常数级别的,时间复杂度为O(nlogn)

#include<iostream>
#include<math.h>
#include<algorithm>
#include<string.h>
using namespace std;

#define Inf 1<<31-1
const int N = 200005;
struct Point{
	double x;
	double y;
}P[N];
int mpt[300005];
bool cmp1(Point a,Point b)
{
	if(a.x==b.x)return a.y<b.y;
	return a.x<b.x;
}//第一个排序是按照x坐标排序,排序以后才好分左右平面 
bool cmp2(int a,int b)
{
	return P[a].y<P[b].y;
}//第二个排序是选择中间的点,不懂得往下看

double dis(Point x, Point y)
{
	return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}

double solve(int left, int right)
{
	int maxx = Inf;
	if(left == right)return maxx;
	if(left + 1 == right)return dis(P[left], P[right]);
	int mid = (left + right) / 2;
	double d1 = solve(left, mid - 1);
	double d2 = solve(mid, right);
	double d = min(d1, d2);
	int k = 0;
	for(int i = left; i <= right; i++)
	{
		if(fabs(P[i].x - P[mid].x) <= d)
			mpt[++k] = i;
	}
	
	sort(mpt + 1, mpt + 1 + k, cmp2);
	
	for(int i = 1; i <= k; i++)
	{
		for(int j = i + 1; j <= k && P[mpt[j]].y - P[mpt[i]].y <= d; j++)
		{
			if(d > dis(P[mpt[i]], P[mpt[j]]))
				d = dis(P[mpt[i]], P[mpt[j]]);
		}
		
	}
	return d;
	
}

int n;
int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++)
	{
		cin >> P[i].x >> P[i].y;
	}
	
	sort(P + 1, P + 1 + n, cmp1);
	
	printf("%.4f", solve(1, n));
}




今天的分享就到这里啦!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值