BZOJ1069 [SCOI2007] 最大土地面积
@(BZOJ)[凸包, 旋轉卡殼]
Description
在某块平面土地上有\(N\)个点,你可以选择其中的任意四个点,将这片土地围起来,当然,你希望这四个点围成
的多边形面积最大。
Input
第1行一个正整数\(N\),接下来\(N\)行,每行\(2\)个数\(x\),\(y\),表示该点的横坐标和纵坐标。
Output
最大的多边形面积,答案精确到小数点后\(3\)位。
Sample Input
5
0 0
1 0
1 1
0 1
0.5 0.5
Sample Output
1.000
HINT
数据范围 \(n<=2000\), $ |x|, |y|<=100000$
Solution
模板題
主要用於學習Graham掃描法求凸包, 叉積判斷向量的關係以及叉積計算三角形面積.
至於所謂的旋轉卡殼做法, 在這一題上實際上體現得並不深刻. 可以作為一個初步的理解吧.
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 1 << 11;
const double INF = 1e30;
int n;
struct point
{
double x, y;
point(){}
inline point(double _x, double _y)
{
x = _x, y = _y;
}
inline friend int operator <(const point &a, const point &b)
{
return a.x == b.x ? a.y < b.y : a.x < a.y;
}
inline friend point operator -(const point &a, point &b)
{
return point(a.x - b.x, a.y - b.y);
}
}a[N];
point orig;
inline double det(point a, point b)
{
return a.x * b.y - a.y * b.x;
}
inline int cmp(point a, point b)
{
return det(a - orig, b - orig) > 0;
}
int top;
point stack[N];
inline void graham() //求凸包
{
int p = 0;
for(int i = 1; i < n; i ++)
if(a[i] < a[p])
p = i;
swap(a[0], a[p]);
orig = a[0];
sort(a + 1, a + n, cmp);
top = 0;
stack[top ++] = a[0], stack[top ++] = a[1];
for(int i = 2; i < n; i ++)
{
while(top && det(stack[top - 1] - stack[top - 2], a[i] - stack[top - 1]) <= 0)
top --;
stack[top ++] = a[i];
}
}
inline double getArea(point a, point b, point c)
{
return fabs(det(a - c, b - c) / 2);
}
inline double solve() //旋轉卡殼, 利用的是凸包的單調性.
{
double ans = - INF;
for(int i = 0; i < top; i ++) //枚舉一條對邊
{
int a = i % top, b = (i + 2) % top; //發現另一條對邊的端點具有單調性
for(int j = i + 2; j < top; j ++)
{
while((a + 1) % top != j && getArea(stack[i], stack[j], stack[a]) < getArea(stack[i], stack[j], stack[(a + 1) % top]))
a = (a + 1) % top;
while((b + 1) % top != i && getArea(stack[i], stack[j], stack[b]) < getArea(stack[i], stack[j], stack[(b + 1) % top]))
b = (b + 1) % top;
ans = max(ans, getArea(stack[i], stack[j], stack[a]) + getArea(stack[i], stack[j], stack[b]));
}
}
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ1069.in", "r", stdin);
freopen("BZOJ1069.out", "w", stdout);
#endif
scanf("%d", &n);
for(int i = 0; i < n; i ++)
scanf("%lf%lf", &a[i].x, &a[i].y);
graham();
printf("%.3lf", solve());
}