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
Data Range
保证:\(n \leq 2000, |x|, |y| \leq 10^5\)
Explanation
考虑每一条经过任意两个点的斜线, 我们如果令这个四边形中一定有这一条直线作为对角线, 那么必要地, 一定需要另外两个点离这两个点尽量远.
也就是说, 另外两个点一定在凸包上. 所以我们先用 Graham 维护出所有点构成的点阵的 凸包, 然后递归每一个对角线, 复杂度 \(O(n^2)\), 然后在均摊 \(O(1)\) 的时间内转移最佳 的 (上、下) 两个点. 这两个点与对角线构成一个四边形, 记一下面积就可以了.
这道题里面我写的随机访问栈在实际应用中还是很好用的, 另外还可以用负数来寻址, 在搞几何凸包的时候不知道高到哪里去了~ 这个概念是从 Python 那里借鉴过来的 233
Source Code
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#define sqr(__x) ((__x)*(__x))
using namespace std;
typedef long long lli;
typedef long double llf;
const int maxn = 2010;
template <typename _T>
class random_access_stack
{
private:
// vector<_T> data;
_T data[maxn];
int a_size;
public:
void pop() {
// data.erase(--data.end());
if (a_size > 0) a_size--;
return ; }
void push(_T in) {
// data.push_back(in);
data[++a_size] = in;
return ; }
_T& top(void) {
// if (this->empty()) {
// _T* zero = new _T; return *zero; }
// return *--data.end(); }
return data[a_size]; }
_T get(void) {
// _T ret = this->top();
// this->pop();
// return ret; }
return data[a_size--]; }
bool empty(void) {
// return data.empty(); }
return a_size <= 0; }
void clear(void) {
a_size = 0; return ; }
size_t size(void) {
// return data.size(); }
return a_size; }
_T& operator[] (int pos) {
// if (pos <= 0 || pos > this->size()) {
// _T* zero = new _T; return *zero; }
// return data[pos - 1]; }
if (pos <= -1) return data[a_size + 1 + pos];
return data[pos]; }
};
struct coord { llf x, y;
coord(llf _x, llf _y) { x = _x, y = _y; }
coord(void) { x = 0, y = 0; } };
coord operator - (coord a, coord b) {
return coord(a.x - b.x, a.y - b.y); }
llf operator * (coord a, coord b) {
return a.x * b.y - a.y * b.x; }
llf dist(coord a, coord b) { // Not exactly distance, but its ^2.
return sqr(a.x - b.x) + sqr(a.y - b.y); }
int n;
coord pnt[maxn];
random_access_stack<coord> stk;
bool cmp(coord a, coord b) {
double perpendicular =
(a - pnt[1]) * (b - pnt[1]);
if (perpendicular == 0)
return dist(a, pnt[1]) < dist(b, pnt[1]);
return perpendicular < 0;
}
void eval_bounds(void)
{
// Using Graham algorithm to evaluate this
int select = 1; // Selected top-left point, top prioritized
for (int i = 2; i <= n; i++)
if (pnt[i].y < pnt[select].y || (pnt[i].y == pnt[select].y &&
pnt[i].x < pnt[select].x))
select = i;
// Sorting these points according to slope
swap(pnt[1], pnt[select]);
sort(pnt + 2, pnt + n + 1, cmp);
// Selecting points that composes the outer bounds of this geometry
stk.clear();
stk.push(pnt[1]); stk.push(pnt[2]);
for (int i = 3; i <= n; i++) {
while (stk.size() > 1 && (pnt[i] - stk[-2]) * (stk[-1] - stk[-2]) <= 0)
stk.pop();
stk.push(pnt[i]);
}
return ;
}
llf eval_result(void)
{
stk.push(pnt[1]);
llf res = 0;
int m = stk.size() - 1;
for (int x = 1; x <= m; x++) {
int a = x % m + 1, b = (x + 2) % m + 1;
// a is the top-point, whereas b serves the opposite.
for (int y = x + 2; y <= m; y++) {
while (a % m + 1 != y && (stk[y] - stk[x]) * (stk[a+1] - stk[x]) >
(stk[y] - stk[x]) * (stk[a] - stk[x]))
a = a % m + 1;
while (b % m + 1 != x && (stk[b+1] - stk[x]) * (stk[y] - stk[x])>
(stk[b] - stk[x]) * (stk[y] - stk[x]))
b = b % m + 1;
res = max(res, (stk[y] - stk[x]) * (stk[a] - stk[x]) + (stk[b] - stk[x]) * (stk[y] - stk[x]));
}
}
return res;
}
int main(int argc, char** argv)
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%Lf%Lf", &pnt[i].x, &pnt[i].y);
eval_bounds();
llf result = eval_result() / 2.0;
printf("%.3Lf\n", result);
return 0;
}