服务器崩了,资料还没备份,回到博客园
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\).
也就是说每个基点最多也才枚举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\n", solve(1, n) / 2.0); } return 0; }
来源:https://www.cnblogs.com/caibingxu/p/12536369.html