分治思想:划分子问题,解决子问题,合并子问题
题目:UVA 10245
题意:在一个二维平面内给定n个点,求最近的两个点的距离。(n≤10000)
题解:直接暴力枚举所有点是肯定行不通的。那么基于分治的思想:按照横坐标排序后,分成两个部分,那么最近距离的点对就是以下的情况
(1)两个点均属于一个区域
(2)两个点属于不同区域
对于(1)的情况,我们可以直接递归求得,因此关键在于对于第二情况的处理,直接处理的话并没有比原来问题简单多少。因此将(2)的描述修改一下
(·2)两个点来自不同区域,并且横坐标距离<d(d为情况(1)的最小长度)
在(2)的基础上加了横坐标距离<d的限制。因为在(1)之中我们已经找到了最短距离d,因此横坐标距离 ≥ d也就无需再考虑。
首先,所有的点已经以横坐标为关键字排序完成,取划分界限mid,从mid往左枚举到mid的横坐标距离<d的点,再枚举mid往右边走到mid的横坐标距离<d的点,对它们进行一一配对求得答案。就把整个平面缩成了[mid-d, mid+d],只需要考虑区间里面的点即可。
(或许大家比较担心一种情况,万一初现这个区间内的点很多呢?但是书的回答是:“d经过了很多次的递归变得很小,所以不考虑”)
这样,递归的深度是O(logn),每层处理复杂度为O(n),而总的时间复杂度就是O(nlogn)
源代码:
#include <stdio.h>
#include <math.h>
#include <algorithm>
using namespace std;
const int N = 1e4+5;
const int inf = 0x3f3f3f3f;
struct node
{
double x, y;
}a[N];
bool cmpx(node a, node b)
{
return a.x<b.x;
}
double dist(node a, node b)
{
return sqrt(pow(a.x-b.x, 2)+pow(a.y-b.y, 2));
}
double solve(int l, int r)
{
if(l==r) return inf;
if(l+1==r) return dist(a[l], a[r]);
//划分解决
int mid = (l+r)>>1;
double st = a[mid].x;//划分依据
double d = min(solve(l, mid), solve(mid+1, r));
//在矩形空间内,左边的点跟右边的点一一匹配,合并
for(int i = mid; i >= l && st-a[i].x < d; i --)
for(int j = mid+1; j <= r && a[j].x-st < d; j ++)
d = min(d, dist(a[i], a[j]));
return d;
}
int main()
{
int n;
while(scanf("%d", &n), n)
{
for(int i = 0; i < n; i ++)
scanf("%lf%lf", &a[i].x, &a[i].y);
sort(a, a+n, cmpx);
double ans = solve(0, n-1);
if(ans<10000.0) printf("%.4f\n", ans);
else puts("INFINITY");
}
return 0;
}