P7883 平面最近点对(加强加强版) 题解
这么好的题为什么没有简单一点的乱搞做法啊!
以下来自某篇题解:
提一下几个要点:
1.是按照 \(x\times y\) 从小到大排序。
2.能往后多查找几个点就多查找几个点(在时间允许的情况下)。
3.必须旋转,否则 \(147\) 分。
但是旋转是什么,蒟蒻不懂……
这里提供一种对于蒟蒻更加友好的做法。
前置芝士
- 计算两点之间的欧几里得距离;
- 按照自定义排序方式使用 sort 排序。
其实这种做法本质上也是排序后乱搞,但是排序方式上有一些细微差别,代码也要简洁不少。
解题思路
我们先按照 \(x\) 从小到大,\(x\) 相同时 \(y\) 从大到小的顺序对于所有点排序。我们充分发挥人类智慧,大胆猜测最近点对一定是某点对周围的某些点对。为了避免重复检验,我们每次之枚举若干个在这个点之后的点,并更新最小值, 最后就能得到答案了。但是具体需要枚举多少个点呢?
观察数据范围,我们发现 \(n\) 还是有亿点大的,那么我们考虑将 \(n\) 分两种情况考虑:
- \(n\le 2\times 10^5\),这个时候我们可以枚举 \(2000\) 个数;
- \(n\gt 2\times 10^5\),这个时候我们可以枚举 \(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);
}