vijos1012 平面最近点对(模板)
半年没摸键盘了,为了PKUSC要做恢复性训练了
裸的平面最近点对模板题,要用到分治算法,O(nlogn),但是这道题暴力也能过
暴力
纯暴力肯定用不了,要用到一些剪枝
- 先把数组以x为第一次序,y为第二优先次序排序
- 在两层循环中,如果p[i].x和p[j].x的差值已经大于当前算的最小距离了,那以后的就肯定不能取了,直接剪掉
// 100
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int n;
const int MAXN = 100000 + 10;
const int INF = 0x3f3f3f3f;
struct node
{
ll x, y;
} p[MAXN];
bool cmp(node a, node b)
{
if(a.x == b.x) return a.y <= b.y;
else return a.x < b.x;
}
double dist(int a, int b)
{
return sqrt((double)((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y)));
}
int main()
{
// freopen("point.in", "r", stdin);
cin >> n;
for(int i = 1; i <= n; i++)
cin >> p[i].x >> p[i].y;
sort(p+1, p+1+n, cmp);
double ans = INF;
for(int i = 1; i <= n; i++)
for(int j = i+1; j <= n; j++)
{
if((p[j].x-p[i].x) > ans) break;
ans = min(ans, dist(i, j));
}
printf("%.3f", ans);
return 0;
}
分治
- 肯定还是要排序
- solve(i,j)表示点i到j之间的所有点可以形成的最小点对大小
- 计算时运用分治法可以大量简化时间复杂度(我个人觉得这实际上是一种DP)
- 对于一段区间[i,j],找到他们的中间点mid = (i + j) >> 1
- 然后分别计算左右两区间的最小点对大小,再进行比较,即
d = min(solve(i, mid), solve(mid+1, j))
- 到这一步当然还不够,如果这两个区间之间的点对存在更小的情况呢?
- 先取出中间点的横坐标midx
- 因为事先已经排好序,所以如果存在上文说到的点对,则他们的横坐标的范围顶多在(midx-d, midx+d)之间
- 把所有这样的点都找出来,再在这些点之间找出最小距离值和d比较就可以了
- 再找这些点的时候可以用到与上面暴力相似的优化
代码如下
//AC
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
const int MAXN = 100000 + 10;
const int INF = 0x3f3f3f3f;
struct node
{
long long x, y;
} p[MAXN];
node temp[MAXN];
bool cmp(node a, node b)
{
if(a.x == b.x) return a.y <= b.y;
else return a.x < b.x;
}
//double dist(int a, int b)
//{
// return sqrt((double)((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y)));
//}
double dist(node a, node b)
{
return sqrt((double)((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}
double jue(long long a)
{
if(a >= 0) return a;
else return -a;
}
bool cmpy(node a, node b)
{
return a.y < b.y;
}
double solve(int lt, int rt)
{
double ans = INF;
if(lt == rt) return ans;
int mid = (lt + rt) >> 1;
double ans1 = solve(lt, mid);
double ans2 = solve(mid+1, rt);
ans = min(ans1, ans2);
int midx = p[mid].x;
int ll = midx - ans, rr = midx + ans;
int ld = p[lt].x, rd = p[rt].x;
int cnt = 0;
// for(int i = 1; i <= n; i++)
// if(p[i].x >= ll && p[i].x <= rr && p[i].x >= ld && p[i].x <= rd) temp[++cnt] = p[i];
for(int i = lt; i <= rt; i++)
if(jue(p[mid].x-p[i].x) <= ans) temp[++cnt] = p[i];
sort(temp+1, temp+1+cnt, cmpy);
for(int i = 1; i <= cnt; i++)
for(int j = i+1; j <= cnt; j++)
{
node u = temp[i], v = temp[j];
if((v.y-u.y) > ans) break;
double ans3 = dist(u, v);
ans = min(ans, ans3);
}
return ans;
}
int main()
{
freopen("point.in", "r", stdin);
cin >> n;
for(int i = 1; i <= n; i++)
cin >> p[i].x >> p[i].y;
sort(p+1, p+1+n, cmp);
printf("%.3f", solve(1, n));
}