服务器崩了,资料还没备份,回到博客园
HDU1007
题意:
给你一些在一个平面上的点的坐标,让你找出这堆点中,距离最短的两个点,距离的一半为多少?
题解:
数据 (n=10^6),如果我们枚举每个点的话,很明显复杂度将会达到(n^2),这样必定会超时。
考虑使用分治。
- 首先我们把点按照(x)轴坐标从小到大排序
- 选取一个中点(mid),将目前所有的点分成左右两边((1,mid),(mid+1,n))
- 目前可以推出结果只有三种情况
- 两个点都在左边
- 两个点都在右边
- 一个在左边,一个在右边
- 目前可以推出结果只有三种情况
- 针对结果(1)和结果(2)我们都能递归求出,递归边界为当目前点的个数为(2)或者(3)的时候,我们可以直接算出结果。
这样我们目前得到了(Lmin)和(Rmin)。
接下来处理第三种结果。
由于我们先前得出了(Lmin)和(Rmin),由此得(dis=min(Lmin,Rmin))。
如果要验证第三种结果,必然由是一个左边的点连接右边的点,并且距离最短,那么我们自然是选取(mid)点。
接下来在目前所有点选出和(mid)点的 (x)轴距离 不大于(dis)的所有点。(如果连(x)轴距离都大于dis,算上(y)轴距离只会更远)
把选出来的点按(y)轴坐标从小到大排序,然后枚举这些点即可。
中间还可进一步剪枝,如果(v)点到达基点的距离已经大于(dis),那么就可以更换下一个基点。
额外知识点(有兴趣可以了解)
在刚才的最后一步枚举中,在算法导论中有解释过。
我们将点选出来后,这些点自然是处于一个 (x)轴距离为 (2*dis)的一块区域。
选取一个点作为 基点 ,和他距离小于 (dis)的其它点,自然 (y)轴距离也不会大于 (dis)
于是我们针对每一个 基点可以得出一个区域,并且这个区域内左右两边最多4个点,总和为8个点。
因为如果你左右任意一边多出一个点,那么不满足之前求出 (Lmin Rmin).
也就是说每个基点最多也才枚举8次。
#include <bits/stdc++.h>
//freopen("in.txt", "r", stdin);
using namespace std;
typedef double dou;
typedef long long ll;
typedef pair<ll, ll> pii;
#define M 200050
#define inf 0x3f3f3f3f
#define mod 998244353
#define W(a) while(a)
#define lowbit(a) a&(-a)
#define left k<<1
#define right k<<1|1
#define ms(a,b) memset(a,b,sizeof(a))
#define debug(a) cout<<#a<<" == "<<a<<endl
#define false_stdio ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
int n;
struct Data {
dou x;
dou y;
}node[M];
bool cmpx(Data &a, Data &b) { return a.x < b.x; }
bool cmpy(Data &a, Data &b) { return a.y < b.y; }
dou dis(Data& a, Data &b) { return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y)); }
dou solve(int L, int R) {
if (L + 1 == R)return dis(node[L], node[R]);//如果目前只有两个点
if (L + 2 == R)return min(dis(node[L], node[L + 1]), min(dis(node[L], node[R]), dis(node[L + 1], node[R])));//如果目前只有三个点
int mid = L + R >> 1;
dou ans = min(solve(L, mid), solve(mid + 1, R));//递归求左右两边最小距离
//选点
vector<Data>tmp;
for (; L <= R; L++) {
if (fabs(node[mid].x - node[L].x) <= ans) {
tmp.push_back(node[L]);
}
}
sort(tmp.begin(), tmp.end(), cmpy);//按y坐标排序
//枚举点
for (int i = 0; i < tmp.size(); i++) {
for (int j = i + 1; j < tmp.size(); j++) {
if (tmp[j].y - tmp[i].y >= ans)break;
ans = min(ans, dis(tmp[i], tmp[j]));
}
}
return ans;
}
int main() {
false_stdio;//HDU貌似关闭同步cin还是慢,所以还是用scanf了
W(scanf("%d", &n) != EOF && n) {
for (int i = 1; i <= n; i++) scanf("%lf%lf", &node[i].x, &node[i].y);
sort(node + 1, node + n + 1, cmpx);//按x坐标排序
printf("%.2lf
", solve(1, n) / 2.0);
}
return 0;
}