洛谷题单指南-分治与倍增-P1257 平面上的最接近点对

原题链接:https://www.luogu.com.cn/problem/P1257

题意解读:

解题思路:

1、暴力法

枚举每两个点,更新距离最短值

2、分治法

将点按x坐标排序,a[i].x,a[i].y表示第i个点的x,y坐标

将点划分为两部分,设左半部分为A,右半部分为B

两个距离最短的点有三种可能:两个点都在A;两个点都在B;一个点在A,一个点在B。

因此,可以采用分治法求解!

设left是第一个点编号,right是最后一个点编号

中间点mid = (left + right) / 2

对两部分分别计算最短距离,对部分递归求解:left~mid,mid+1~right,得到左边两点最短距离minleft,右边两点最短距离minright

再考虑第三种情况,一个点在左边、一个点在右边的情况:

d = min(minleft, minright)

左、右两边的点只可能在a[mid].x - d  ~ a[mid].x + d的范围内,因为左右两边的点距离如果超出d就没有意义

将a[mid].x - d  ~ a[mid].x + d范围内的点按y坐标排序

计算是否有两个点距离比d小,更新最小值。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 10005;
struct node 
{
    int x, y;
} a[N], tmp[N];
int n;

bool cmpx(node &a, node &b)
{
    return a.x < b.x;
}

bool cmpy(node &a, node &b)
{
    return a.y < b.y;
}

double dist(node &a, node &b)
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

double min_dist(int left, int right)
{
    if(left == right) return 2e9;
    if(left + 1 == right) return dist(a[left], a[right]);
    int mid = (left + right) / 2;
    double leftmin = min_dist(left, mid);
    double rightmin = min_dist(mid + 1, right);
    double d = min(leftmin, rightmin);
    int cnt = 0;
    for(int i = left; i <= right; i++)
    {
        if(fabs(a[i].x - a[mid].x) <= d) 
            tmp[++cnt] = a[i];
    }
    sort(tmp + 1, tmp + cnt + 1, cmpy);
    for(int i = 1; i < cnt; i++)
    {
        for(int j = i + 1; j <= cnt && fabs(tmp[i].y - tmp[j].y) < d; j++)
        {
            d = min(d, dist(tmp[i], tmp[j]));
        }
    }
    return d;
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i].x >> a[i].y;
    sort(a + 1, a + n + 1, cmpx);
    double ans = min_dist(1, n);
    cout << fixed << setprecision(4) << ans;
    return 0;
}

 

posted @ 2024-09-18 14:30  五月江城  阅读(27)  评论(0编辑  收藏  举报