题目大意:给出平面上的n个点,找出距离最近的两个点。
由于n的取值比较大(n<10000),考虑使用分治法。大概就是把平面上的点分成两部分,分别处理左半部分L、右半部分R以及跨越分界线M的点对,分别求得左半部分内点的最近距离lmin和右半部分的最近距离rmin,令d=min(lmin, rmin),然后处理跨越分界线的点对,由于只需考虑比距离比d值小的点对,所以我们只考察点的x坐标在[M-d, M+d]范围内的点,找出其中的最短距离并更新即可。
1 #include <cstdio> 2 #include <cmath> 3 #include <algorithm> 4 using namespace std; 5 #define MAXN 10000+10 6 7 struct Point 8 { 9 double x, y; 10 }; 11 Point point[MAXN], tmp[MAXN]; 12 13 bool cmp1(const Point& a, const Point& b) 14 { 15 if (a.x != b.x) return a.x < b.x; 16 return a.y < b.y; 17 } 18 19 bool cmp2(const Point& a, const Point& b) 20 { 21 if (a.y != b.y) return a.y < b.y; 22 return a.x < b.x; 23 } 24 25 double dis(const Point& a, const Point& b) 26 { 27 double t1 = (a.x - b.x) * (a.x - b.x); 28 double t2 = (a.y - b.y) * (a.y - b.y); 29 return sqrt(t1+t2); 30 } 31 32 double conquer(int left, int right) 33 { 34 if (left == right) return 10010; 35 if (right - left == 1) return dis(point[left], point[right]); 36 int mid = (left + right) / 2; 37 double left_min = conquer(left, mid); 38 double right_min = conquer(mid+1, right); 39 double d = min(left_min, right_min); 40 int cnt = 0; 41 for (int i = left; i <= right; i++) 42 if (fabs(point[i].x-point[mid].x) < d) 43 tmp[cnt++] = point[i]; 44 sort(tmp, tmp+cnt, cmp2); 45 for (int i = 0; i < cnt; i++) 46 for (int j = i+1; j < i+7 && j < cnt; j++) 47 { 48 double t = dis(tmp[i], tmp[j]); 49 if (t < d) d = t; 50 } 51 return d; 52 } 53 54 int main() 55 { 56 #ifdef LOCAL 57 freopen("in", "r", stdin); 58 #endif 59 int n; 60 while (scanf("%d", &n) != EOF && n) 61 { 62 for (int i = 0; i < n; i++) 63 scanf("%lf%lf", &point[i].x, &point[i].y); 64 sort(point, point+n, cmp1); 65 double ans = conquer(0, n-1); 66 if (ans > 10000) printf("INFINITY "); 67 else printf("%.4lf ", ans); 68 } 69 return 0; 70 }
关于在考察跨越分界线的点对时所作的优化,可参考这里。