题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1007
题目大意:题目半天没看懂,看了一下数据,手动算了一下,是最近点距离的一半。(最近点对问题)
分析:方法1、暴力,时间复杂度为O(n²),效率太低了
方法2、分治,时间复杂度为O(nlogn),分治包含三个步骤:划分,解决,合并
划分:根据x坐标的中点,将点分为左右两边,当坐标点数<=3时,直接暴力算出最近点对
解决:递归划计算,最后d=min(dl,dr);
合并:合并的时候就出先一个新的问题,可能构成最近点对的两个点分别在划分的左右两边,如果直接处理,那么就和暴力的方法差不多,没有优化多少,可 以考虑用一下上一步得出来的目前的最近点对的距离d,用这个距离d来减少点的数量,这样只需要考虑横坐标 x0 - d < x < x0 + d 的点和纵坐标 y0 - d < y < y 的点,这样的限制条件,最后在限定区域内的点的数量为5
注: 在划分前需要用sort排序
归并两个排序好的数列可以用inplace_merge()函数,具体用法见代码
ac代码
#include<stdio.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<vector>
#define Max 100005
#define INF 100000000
using namespace std;
typedef pair<double, double> P;
int N;
P A[Max];
bool compare_y(P a, P b)
{
return a.second < b.second;
}
double closest_pair(P *a, int n)
{
if(n <= 1)return INF;
int m = n / 2;
double x = a[m].first;
double d = min(closest_pair(a, m), closest_pair(a + m, n - m));
inplace_merge(a, a + m, a + n, compare_y);
vector<P> b;
for(int i = 0; i < n; i++)
{
if(fabs(a[i].first - x) >= d)continue;
for(int j = 0; j < b.size(); j++)
{
double dx = a[i].first - b[b.size() - j - 1].first;
double dy = a[i].second - b[b.size() - j - 1].second;
if(dy >= d)break;
d = min(d, sqrt(dx * dx + dy * dy));
}
b.push_back(a[i]);
}
return d;
}
int main()
{
int N;
while(scanf("%d", &N) && N)
{
for(int i = 0; i < N; i++)
scanf("%lf %lf", &A[i].first, &A[i].second);
sort(A, A + N);
double dis = closest_pair(A, N) / 2;
printf("%.2f\n", dis);
}
return 0;
}
参考书籍:《挑战程序设计竞赛》第2版