Title

P7883 平面最近点对(加强加强版) 题解

这么好的题为什么没有简单一点的乱搞做法啊!

以下来自某篇题解:

提一下几个要点:

1.是按照 x×y 从小到大排序。

2.能往后多查找几个点就多查找几个点(在时间允许的情况下)。

3.必须旋转,否则 147 分。

但是旋转是什么,蒟蒻不懂……

这里提供一种对于蒟蒻更加友好的做法。

前置芝士

  1. 计算两点之间的欧几里得距离;
  2. 按照自定义排序方式使用 sort 排序。

其实这种做法本质上也是排序后乱搞,但是排序方式上有一些细微差别,代码也要简洁不少。

解题思路

我们先按照 x 从小到大,x 相同时 y 从大到小的顺序对于所有点排序。我们充分发挥人类智慧,大胆猜测最近点对一定是某点对周围的某些点对。为了避免重复检验,我们每次之枚举若干个在这个点之后的点,并更新最小值, 最后就能得到答案了。但是具体需要枚举多少个点呢?

观察数据范围,我们发现 n 还是有亿点大的,那么我们考虑将 n 分两种情况考虑:

  • n2×105,这个时候我们可以枚举 2000 个数;
  • n>2×105,这个时候我们可以枚举 700 个数;

然后就愉快地 AC 啦!

AC 代码

这是第一种版本,因为码风恶臭优良,看起来比较长实际也很长

#include<math.h>
#include<stdio.h>
#include<valarray>
#include<stdlib.h>
#include<algorithm>
#define double long long
#define inf 1e18+7
#define N 400005
struct Point{
    double x;
    double y;
    inline bool operator <(
        const Point &B
    ) const {
        if(x!=B.x)
            return x<B.x;
        return y>B.y;
    }
}a[N];int n;
double mint=inf;
inline double dis(
    const double &x1,
    const double &y1,
    const double &x2,
    const double &y2
){
    double Ox=(x1-x2)*(x1-x2);
    double Oy=(y1-y2)*(y1-y2);
    return Ox+Oy;
}
inline double dis(
    const Point &A,
    const Point &B
){
    double x1=A.x,y1=A.y;
    double x2=B.x,y2=B.y;
    return dis(x1,y1,x2,y2);
}
inline void UpdateMin(
    const int &x
){
    int l=x+1,r=x+700;
    if(n<200000) r=x+2000;
    if(r>n) r=n;
    for(
        int i=l;
        i<=r;++i
    ){
        double now=dis(a[x],a[i]);
        if(now<mint) mint=now;
    }
}
signed main(){
    scanf("%d",&n);
    for(
        int i=1;
        i<=n;++i
    ){
        scanf("%lld",&a[i].x);
        scanf("%lld",&a[i].y);
    }
    std::sort(a+1,a+n+1);
    for(
        int i=1;
        i<=n;++i
    ) UpdateMin(i);
    printf("%lld",mint);
}

这是第二种,删去了一些无用的东西,现在就很短了。

#include<bits/stdc++.h>
#define ll long long
#define inf 1e18+7
#define N 400005
struct Point{ll x,y;}a[N];
inline bool cmp(Point x,Point y){
    return (x.x^y.x)?(x.x<y.x):(x.y>y.y);
}int n;ll mint=inf;
inline ll dis(ll x1,ll y1,ll x2,ll y2){
    return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}inline void UpdateMin(int x){
    int l=x+1,r=n<200000?x+2000:x+700;
    if(r>n) r=n;for(int i=l;i<=r;++i){
        ll now=dis(a[x].x,a[x].y,a[i].x,a[i].y);
        if(now<mint) mint=now;
    }
}signed main(){
    scanf("%d",&n);for(int i=1;i<=n;++i)
        scanf("%lld%lld",&a[i].x,&a[i].y);
    std::sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;++i) UpdateMin(i);
    printf("%lld",mint);
}
posted @   UncleSam_Died  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示