初学算法-分治法求平面上最近点对(Closest Pair)-HDU 1007

本来这个算法在笔者电脑里无人问津过一段时间了,但今天正好做HDU 1007见到了这个问题,今天就来把代码分享出来吧!

我们首先将所有点按照坐标x排序一下,再做一条直线l当作“分割线”,方便我们递归。

然后,我们就可以把这些点按照x轴的坐标分为左半部分和右半部分。那么最短距离一定在左半部分、右半部分、跨越左右的点对中的一个。

那么你可能会有疑问了:本来最近点对也一定在这三个区域内,这不还是相当于什么都没干吗?

还真不是。我们可以假设通过递归得到了左边最小距离为d1,右边最小距离为d2,令δ = min(d1,d2)

如图所示,如果跨越左右的点对可能是最短距离,那么它也必然比δ小。而在以l为中心、最大距离为2δ的区域中,最多有O(n)个如图所示的矩形。另外,可以证明对于每个矩形区域,最多尝试8个点对一定能找到最短距离(算法导论第33.4节有详细的证明,这里不再赘述)。

因此,我们可以写出递归式:T(n)=2T(n/2)+O(n),可以用主项定理(master method)解得时间复杂度T(n)=O(nlogn)。加上排序一次的时间O(nlogn),因此整个算法的运行时间T(n)‘ = T(n)+O(nlogn) = O(nlogn)。

下面,通过这个算法,我们就可以写出一份代码来:

/**
 * Find closest distance in N points.
 * Time cost: O(nlogn)
 * Author: Zheng Chen / Arclabs001
 * Copyright 2015 Xi‘an University of Posts & Telecommunications
 */

/**
 * Algorithm:
 * First of all, sort the points in ascending order by x.
 * Divide the array into 2 parts, and find the smallest distance within two parts.
 * Let min as the smaller one, and find the smallest split distance.
 */
#include <iostream>
#include <algorithm>
#include <cmath>
#define INF 0x6FFFFFFF
using namespace std;

struct Node{
	double x, y;

	friend bool operator < (const Node &a, const Node &b){
		if(a.x == b.x)
			return a.y < b.y;
		return a.x < b.x;
	}
};

Node* Point = NULL;
/**
 * Calculate the distance between two points.
 * @return   [double, the distance between a and b]
 */
double _distance(const Node a, const Node b)
{
	return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}

double smaller(double p, double q)
{
	return (p > q) ? q : p;
}
/**
 * [Find the closest distance, divide & conquer]
 * @param  left  [search from where]
 * @param  right [to where]
 * @return       [double, the smallest distance in the whole set of points]
 */
double Closest_distance(int left, int right)
{
	double d = INF;
	double distance_tmp;

	if(left == right)
		return 0;
	if(right == left+1)
		return _distance( Point[left], Point[right] );
	int mid = (left + right) / 2;

	d = smaller( Closest_distance(left,mid) , Closest_distance(mid,right) );

	for(int i=mid-1; i>=left && Point[mid].x - Point[i].x < d; i--){
		for(int j = mid+1; j<=right && Point[j].x - Point[mid].x < d && fabs( Point[i].y - Point[j].y) < d; j++){
			distance_tmp = _distance( Point[i], Point[j] );
			if(distance_tmp < d)
				d = distance_tmp;
		}
	}

	return d;
}

int main()
{
    int n;
    cin>>n;
    Point = new Node[n];
    for(int i=0; i<n ; i++){
    	cin>>Point[i].x>>Point[i].y;
    }
    sort(Point,Point+n);
    cout<<Closest_distance(0,n-1)<<endl;
    return 0;
}

当然,直接套用这个代码提交到HDOJ会导致Exceed Time Limit,你懂的^_^

时间: 2024-10-14 19:13:38

初学算法-分治法求平面上最近点对(Closest Pair)-HDU 1007的相关文章

poj 3714 Raid 分治法求平面最近点对

题意: 给平面上的n个点,求两点间的最短距离. 分析: 分治法,保存点用vector会tle... 代码: //poj 3714 //sep9 #include <iostream> #include <algorithm> #include <cmath> using namespace std; const double INF=1e50; struct P { double x,y; int type; }p[240000],b[240000]; bool cmp

基础算法 分治法求最大最小元

思路:运用分治的思想,将要排序的整个数组从中间劈开,分别求其左右两边的最大最小值,然后将求出的最大最小值合起来进行比较. 当左右两边的数组小到一定程度时: (1)数组中只有一个元素,maxNum=minNum; (2)数组中有两个元素,找出两个元素中的最大最小值; (3)数组中大于两个元素,从中间分开,继续递归; 1 #include<stdio.h> 2 #include<iostream> 3 #include<stdlib.h> 4 using namespace

《github一天一道算法题》:分治法求数组最大连续子序列和

看书.思考.写代码! /*************************************** * [email protected] * blog: http://blog.csdn.net/hustyangju * 题目:分治法求数组最大连续子序列和 * 思路:分解成子问题+合并答案 * 时间复杂度:O(n lgn) * 空间复杂度:O(1) ***************************************/ #include <iostream> using nam

分治法求众数问题 (配图)

分治法求众数问题 (配图) 采用分治法,以中间为界限, 先计算围绕中间这个数字的众数情况,然后左右分开递归计算结果,取最值即可. 左右递归计算的时候要先做判断,假如左边或是右边的个数都比已求的重数小,就没必要计算了,即使左边或是右边全部都是一样的,那么他的重数也是小于已求的,所以没必要进行运算,这一周在加深分治算法的学习,这题着实花了我不少时间. 具体代码: // 用分治法求众数 #include <iostream> #include <cstdio> using namespa

分治法求数组的最大值最小值

实现求数组的最大值最小值,蛮力法要容易的多.本着重在体验分治法的思想的原则: 1 int main(void) 2 { 3 void Maxmin(int a[],int low,int high,int maxmin[2]); 4 int a[10],maxmin[2]; 5 6 printf("Enter 10 integer numbers:\n"); 7 for(int i=0;i<10;i++) 8 scanf("%d",a+i); 9 10 Max

HDU ACM 1007 Quoit Design 分治法求最近点对

题意:给n个点的坐标,求距离最近的一对点之间距离的一半. 分析:分治法求最近点对. #include<iostream> #include<algorithm> #include<cmath> using namespace std; #define N 100005 double min(double a,double b) { return a<b?a:b; } struct POINT { double x,y; }; POINT point[N],*px[

算法笔记_065:分治法求逆序对(Java)

目录 1 问题描述 2 解决方案 2.1 蛮力法 2.2 分治法(归并排序)   1 问题描述 给定一个随机数数组,求取这个数组中的逆序对总个数.要求时间效率尽可能高. 那么,何为逆序对? 引用自百度百科: 设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同. 如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数. 例如,数组(3,1,4,5,

[算法]:分治法-求大整数相乘

#问题大整数相乘 #思路说明 对于大整数计算,一般都要用某种方法转化,否则会溢出.但是python无此担忧了. Python支持**"无限精度"的整数,**一般情况下不用考虑整数溢出的问题,而且Python Int类型与任意精度的Long整数类可以无缝转换,超过Int 范围的情况都将转换成Long类型. 例如: >>> 2899887676637907866*1788778992788348277389943 51872581574157002360341697913

算法---分治法

1.分治法的思想: 将一个输入规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同,然后递归的求解这些子问题,最后用适当的方法将各子问题的解合并成原问题的解. 2.分治法的步骤: 分(divide) 二分为主 治(conquer) 递归调用,当规模足够小时直接处理 组(combine) 3.抽象化控制 procedure DANDC(p,q) global n, A(1:n); integer m, p, q; //1≤p≤q≤n// if SMALL(p,q) //判断输