Plane divide-and-conquer detailed explanation is super detailed (with examples and nearest point-to-point questions (given the title)) (UVA10245, P1257, P1429)

The nearest point pair problem, roughly meaning that there are n points in the plane, find the distance between the two nearest point pairs (plane divide and conquer can be used to solve most cases)

First of all, if you compare one by one, it is the quadratic complexity of n, and many cases will time out

We consider using the divide and conquer method. The general idea is to divide the plane into two halves (horizontal and vertical divisions are fine, here we are talking about dividing vertically according to the horizontal coordinate). Note that we are not according to (the first x coordinate + the
last x coordinate) / 2, because this may degrade the depth of recursion, we should divide according to the x coordinate of the middle point, so as to ensure that the number of points is halved every time recursion.

The idea of ​​divide and conquer is to divide the point into two halves according to the x coordinate. There are two situations:
(1) If the two points are on the left half or the right half, the minimum distance
(2) If the two points are on the left half and the right half respectively shortest distance

For (1) we can get the recursive result back, we focus on (2)
For the minimum distance we get from (1), we record it as d, we only need to consider the case where the distance between two points is less than d (and this Two points are on the left and right sides), so we can ignore the points whose x coordinate difference from the middle point is greater than or equal to d, and we no longer need to consider the same side

There are roughly three ideas for processing (2) (only the method of processing (2) in the deal function is different. In order to save space, the latter two only write the modified deal function)

The first one: For each left point whose x-coordinate difference from the middle point is less than d, we traverse each right point whose x-coordinate difference from the middle point is less than d, and return the minimum value of the distance

Please add a picture description
[It is equivalent to traversing all the points in the dotted line on the left once for all the points in the dotted line on the left. Since the value of d will continue to decrease, there is no need to worry about traversing too many times.

Disadvantages: We have traversed some redundant cases where the y coordinate difference is greater than or equal to d and cannot be the minimum distance

The code is as follows (this consumes 687ms / 3.62MB / 1.52KB in P1429)

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <math.h>
#define INF (double)1e18
using namespace std;
pair<double, double> data[200005];
bool cmp(pair<double, double> p1, pair<double, double> p2)
{
    
    
    if(p1.second == p2.second)
    {
    
    
        return p1.first < p2.second;
    }
    return p2.second < p2.second;
}
double dist(pair<double, double> p1, pair<double, double> p2)
{
    
    
    double num = (p1.first - p2.first) * (p1.first - p2.first) + (p1.second - p2.second) * (p1.second - p2.second);
    return sqrt(num);
}
double deal(int q, int t)
{
    
    
    if(t - q == 1)
    {
    
    
        return INF;
    }
    double a = deal(q, (q + t) / 2);
    double b = deal((q + t) / 2, t);
    double x_mid = data[(q+t)/2].first;
    double d = min(a, b);
    int i = upper_bound(data + q, data + (q + t) / 2, make_pair(x_mid - d, INF)) - data;
    int jj = upper_bound(data + (q + t) / 2, data + t, make_pair(x_mid + d, INF)) - data;
    for(; i < (q + t) / 2; i++)
    {
    
    
        for(int j = (q + t) / 2; j < jj; j++)
        {
    
    
            if(data[i].first - data[j].first >= d)
            {
    
    
                break;
            }
            else
            {
    
    
                d = min(d, dist(data[i], data[j]));
            }
        }
    }
    return d;
}
int main()
{
    
    
    int N;
    scanf("%d", &N);
    for(int i = 0; i < N; i++)
    {
    
    
        scanf("%lf %lf", &data[i].first, &data[i].second);
    }
    sort(data, data + N);
    double jg = deal(0, N);
    printf("%.4lf\n", jg);
}

The second type: we merge and sort according to the y coordinate while recursively processing

In this case, we cannot directly judge which point’s x-coordinate distance from the middle point’s abscissa distance is less than d, so we traverse all points, and when the point’s x-coordinate distance from the middle point’s abscissa distance is less than d, the processing method is , for a certain range of points, perform traversal comparison, as shown in the figure below
insert image description here
[equivalent to we traverse all points once, if the x coordinate is within the range, we traverse the shaded area, due to the restriction of d, so remove itself, the rectangle There are at most 5 more points within the shadow.

Disadvantages: We have traversed all points, and there are redundant cases where the x-coordinate is more than d from the abscissa of the middle point

The code is as follows (this consumes 1.19s / 8.03MB / 1.45KB in P1429)

double deal(int q, int t)
{
    
    
    if(t - q == 1)
    {
    
    
        return INF;
    }
    double x_mid = data[(q+t)/2].first;
    double a = deal(q, (q + t) / 2);
    double b = deal((q + t) / 2, t);
    double d = min(a, b);
    inplace_merge(data + q, data + (q + t) / 2, data + t, cmp);
    vector<int> dian;
    for(int i = q; i < t; i++)
    {
    
    
        if(data[i].first <= x_mid - d || data[i].first >= x_mid + d)
        {
    
    
            continue;
        }
        for(int j = dian.size() - 1; j >= 0; j--)
        {
    
    
            if(data[i].second - data[dian[j]].second >= d)
            {
    
    
                break;
            }
            d = min(d, dist(data[i], data[dian[j]]));
        }
        dian.push_back(i);
    }
    return d;
}

The third type: because the minimum value of the points on the same side has been calculated as d, there is no need to compare the points on the same side again, so we can separate two vector containers to store the points whose x coordinates on the left and right are within the range

But this will cause a problem. If the point on the abscissa of the middle point, we don’t know whether it belongs to the left or the right, so we have to store both the left and right sides, and compare it with the left and right sides. This kind of optimization is not obvious
. , even when there are too many points on the abscissa of the middle point, it will degenerate, and only match up to five times in the original range, and change it to save one or two matches, and increase a lot of code

many disadvantages

The code is as follows (1.58s / 8.07MB / 2.25KB)

double deal(int q, int t)
{
    
    
    if(t - q == 1)
    {
    
    
        return INF;
    }
    double x_mid = data[(q+t)/2].first;
    double a = deal(q, (q + t) / 2);
    double b = deal((q + t) / 2, t);
    double d = min(a, b);
    inplace_merge(data + q, data + (q + t) / 2, data + t, cmp);
    vector<int> right;
    vector<int> left;
    for(int i = q; i < t; i++)
    {
    
    
        if(data[i].first <= x_mid - d || data[i].first >= x_mid + d)
        {
    
    
            continue;
        }
        if(data[i].first <= x_mid)
        {
    
    
            left.push_back(i);
            for(int j = right.size() - 1; j >= 0; j--)
            {
    
    
                if(i == left[j])
                {
    
    
                    continue;
                }
                if(data[i].second - data[right[j]].second >= d)
                {
    
    
                    break;
                }
                else
                {
    
    
                    d = min(d, dist(data[i], data[right[j]]));
                }
            }
        }
        if(data[i].first >= x_mid)
        {
    
    
            right.push_back(i);
            for(int j = left.size() - 1; j >= 0; j--)
            {
    
    
                if(i == left[j])
                {
    
    
                    continue;
                }
                if(data[i].second - data[left[j]].second >= d)
                {
    
    
                    break;
                }
                else
                {
    
    
                    d = min(d, dist(data[i], data[left[j]]));
                }
            }
        }
    }
    return d;
}

Guess you like

Origin blog.csdn.net/m0_52212261/article/details/121052579