BZOJ 3707: 圈地
3707: 圈地
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 236 Solved: 89
[Submit][Status][Discuss]
Description
2维平面上有n个木桩,黄学长有一次圈地的机会并得到圈到的土地,为了体现他的高风亮节,他要使他圈到的土地面积尽量小。圈地需要圈一个至少3个点的多边形,多边形的顶点就是一个木桩,圈得的土地就是这个多边形内部的土地。(因为黄学长非常的神,所以他允许圈出的第n点共线,那样面积算0)
Input
第一行一个整数n,表示木桩个数。
接下来n行,每行2个整数表示一个木桩的坐标,坐标两两不同。
Output
仅一行,表示最小圈得的土地面积,保留2位小数。
Sample Input
3
0 0
0 1
1 0
0 0
0 1
1 0
Sample Output
0.50
HINT
对于100%的数据,n<=1000。
Source
很喵的一道题,据说写个$O(N^{3})$的大暴力就能水过去,黄学长也介绍了一个随机化算法,但出题人还是想了个比较可以接受的正解的。
简单的说就是给定平面上n个点,求这n个点组成三角形的最小面积。
如果分别枚举三个点的话是$O(n^{3})$的,时间无法承受。
如果枚举了两个点a,b。设它们间的距离是L。如果以点a,b所在直线为y轴的话,与其他点所组成的三角形的面积$S=L*|x|/2$,x是其他点在这个坐标系中的横坐标。可以看出面积最小的就是离这个坐标系y轴最近的一个点。如果我们能够快速的得知最近的点的话,就可以将复杂度降低到$O(n^{2})$。
把这些点两两之间求出一条直线,记录这条直线是哪两个点取到的,记录这条直线的斜率k。然后按照k排序,我们可以依次按照k递增连续变化的顺序处理这些直线。
首先将这些点按x坐标排序,相当于按x=0直线排序的情况,考虑由两个相邻的斜率k1变到k2时,这个序列的改变只有k1直线的两个点的顺序交换了下,其它点之间的顺序都没变。在枚举直线时,交换两点维护序列即可。
—— ZRT学长
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int mxn = 1005; 6 7 int n, m, p; 8 9 double ans = 1e200; 10 11 struct Pair 12 { 13 double x, y; 14 15 inline Pair(void) {}; 16 inline Pair(double a, double b) 17 : x(a), y(b) {}; 18 }P[mxn]; 19 20 inline Pair operator - (const Pair &a, const Pair &b) 21 { 22 return Pair(a.x - b.x, a.y - b.y); 23 } 24 25 struct Line 26 { 27 int a, b; 28 double k; 29 30 inline void set(int i, int j) 31 { 32 a = i; 33 b = j; 34 35 k = (P[i].y - P[j].y) / (P[i].x - P[j].x); 36 } 37 }L[mxn * mxn]; 38 39 inline bool cmpP(const Pair &a, const Pair &b) 40 { 41 if (a.x != b.x) 42 return a.x < b.x; 43 else 44 return a.y < b.y; 45 } 46 47 inline bool cmpL(const Line &a, const Line &b) 48 { 49 return a.k < b.k; 50 } 51 52 inline double cross(const Pair &a, const Pair &b) 53 { 54 return a.x * b.y - a.y * b.x; 55 } 56 57 int ps[mxn], rk[mxn]; 58 59 signed main(void) 60 { 61 cin >> n; 62 63 for (int i = 0; i < n; ++i) 64 cin >> P[i].x >> P[i].y; 65 66 sort(P, P + n, cmpP); 67 68 for (int i = 0; i < n; ++i) 69 for (int j = 0; j < i; ++j) 70 L[m++].set(i, j); 71 72 sort(L, L + m, cmpL); 73 74 for (int i = 0; i < n; ++i) 75 rk[i] = ps[i] = i; 76 77 for (int i = 0; i < m; ++i) 78 { 79 int a = L[i].a, b = L[i].b; 80 81 if (ps[a] > ps[b])swap(a, b); 82 83 if (ps[a] > 1 - 1)ans = min(ans, fabs(cross(P[a] - P[b], P[rk[ps[a] - 1]] - P[b]))); 84 if (ps[b] < n - 1)ans = min(ans, fabs(cross(P[a] - P[b], P[rk[ps[b] + 1]] - P[a]))); 85 86 swap(ps[a], ps[b]), swap(rk[ps[a]], rk[ps[b]]); 87 } 88 89 cout << fixed << setprecision(2) << ans / 2.0 << endl; 90 }
@Author: YouSiki