分治入门——平面分治

分治思想:划分子问题,解决子问题,合并子问题

题目: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;
}

猜你喜欢

转载自blog.csdn.net/weixin_38287798/article/details/79116864