Problem
【SCOI2007】最大土地面积
TimeLimit:1Sec
MemoryLimit:128MB
Description
在某块平面土地上有N个点,你可以选择其中的任意四个点,使得这四个点围成的土地面积最大。
第1行一个正整数N,接下来N行,每行2个数x,y,表示该点的横坐标和纵坐标。
Output
最大的多边形面积,答案精确到小数点后3位。
1 2 3 4 5 6
| 5 0 0 1 0 1 1 0 1 0.5 0.5
|
Sample Output
HINT
数据范围: n≤2000,∣x∣,∣y∣≤105
标签:旋转卡壳
Solution
基础旋转卡壳。
首先这四个点一定在凸包上,先求凸包。
考虑枚举每条对角线,找出其两边最远的点,即可找到该对角线对应的最大四边形。
这样就是一个O(n3)的暴力。
枚举对角线的一段,移动另一端,发现两边最远的点都是单调移动的,于是可以带上旋转卡壳,这样内层循环的总复杂度时O(n),就有了一个O(n2)的优质算法。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <bits/stdc++.h> #define MAX_N 2000 using namespace std; typedef double dnt; int n, m; struct pnt { dnt x, y; dnt operator * (const pnt &t) const {return x*t.y-y*t.x;} pnt operator - (const pnt &t) const {return (pnt){x-t.x, y-t.y};} bool operator < (const pnt &t) const {return x == t.x ? y < t.y : x < t.x;} } p[MAX_N+5], c[MAX_N*2+5], mat[MAX_N*2+5][2]; dnt S(pnt a, pnt b, pnt c, pnt d) {return (a*b+b*c+c*d+d*a)/2;} void ConvexHull() { sort(p+1, p+n+1), c[++m] = p[1], c[++m] = p[2]; for (int i = 3; i <= n; c[++m] = p[i++]) while (m > 1 && (p[i]-c[m-1])*(c[m]-c[m-1]) >= 0) m--; int t = m; c[++m] = p[n-1]; for (int i = n-2; i; c[++m] = p[i--]) while (m > t && (p[i]-c[m-1])*(c[m]-c[m-1]) >= 0) m--; m--; for (int i = 1; i <= m; i++) c[i+m] = c[i]; } dnt RotatingCalipers() { dnt ret = 0; if (m == 3) return (c[1]*c[2]+c[2]*c[3]+c[3]*c[1])/2; for (int i = 1; i <= m; i++) { for (int j = i+2, t = j-1; j <= i+m-2; j++) {while (t < j && (c[t+1]-c[t])*(c[j]-c[i]) >= 0) t++; mat[j][0] = c[t];} for (int j = i+m-2, t = j+1; j >= i+2; j--) {while (t > j && (c[t-1]-c[t])*(c[j]-c[i]) <= 0) t--; mat[j][1] = c[t];} for (int j = i+2; j <= i+m-2; j++) ret = max(ret, S(c[i], mat[j][0], c[j], mat[j][1])); } return ret; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%lf%lf", &p[i].x, &p[i].y); return ConvexHull(), printf("%.3lf", RotatingCalipers()), 0; }
|