题意
这题的题目意思很显然,给出多个在二维平面的点,求这些点中所能形成的四边形的最大面积
- 首先要进行的是求这些点的凸包,在凸包上所构建的四边形中存在最大的四边形面积
- 在形成凸包后,有下列三种情况
- 最终所形成的凸包是一条直线,即所有点都在一条直线上,此时形成的凸包的点的个数为2,形成的四边形面积为0
- 最后形成的凸包的点只有三个,此时要构建的是一个凹四边形。那么直接枚举在凸包里面的点,求这个点与其中两个凸包上的点的最小面积。最后用凸包三角形的面积减去这个所求的最小三角形面积即可。
- 形成的凸包点的个数 ≥ 4 ≥4 ≥4 个时,就需要用到旋转卡壳来进行求面积。首先将四边形分割成两个三角形,接下来枚举第一个点与第三个点,用旋转卡壳来定位到当前状态下的最大三角形面积。
- 这道题的数据有坑,有重复出现的点,在对应凸包只有三个点的时候,出现重复的点直接就可以形成最大的凸包三角形面积。
- 详情见代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, int> pii;
template<typename T>
inline void rd(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') {
if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int N = 1e5 + 10;
const int M = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;
int sgn(double x) {
//一个数是否等于0
if (fabs(x) < eps) return 0;
return x < 0 ? -1 : 1;
}
struct Point {
ll x, y;
Point() {
}
Point(ll x, ll y) :x(x), y(y) {
}
Point operator+(const Point& B) {
return Point(x + B.x, y + B.y); }
Point operator-(const Point& B) {
return Point(x - B.x, y - B.y); }
Point operator*(double k) {
return Point(x * k, y * k); }
Point operator/(double k) {
return Point(x / k, y / k); }
bool operator ==(const Point& B) {
return sgn(x - B.x) == 0 && sgn(y - B.y) == 0; }
bool operator <(const Point& B) {
return sgn(x - B.x) < 0 || (sgn(x - B.x) == 0 && sgn(y - B.y) < 0); }
};
typedef Point Vector;//向量
ll Cross(Vector A, Vector B) {
return A.x * B.y - A.y * B.x; }
int Andrew(Point* p, int n, Point* q) {
//p是原图,q是凸包图
sort(p, p + n, [](const Point& a, const Point& b) {
if (a.x == b.x) return a.y < b.y;
return a.x < b.x;
});
n = unique(p, p + n) - p;
int num = 0; //凸包的点数
for (int i = 0; i < n; ++i) {
while (num > 1 && sgn(Cross(q[num - 1] - q[num - 2], p[i] - q[num - 2])) <= 0) --num;
q[num++] = p[i];
}
int tmp = num;
for (int i = n - 2; i >= 0; --i) {
while (num > tmp && sgn(Cross(q[num - 1] - q[num - 2], p[i] - q[num - 2])) <= 0) --num;
q[num++] = p[i];
}
if (n > 1) --num;
return num;
}
inline int nxt(int pos, int n) {
return pos + 1 == n ? 0 : pos + 1;
}
ll RotateCaliper(Point p[], int n) {
ll res = 0;
for (int p1 = 0; p1 < n; ++p1) {
int p2 = nxt(p1, n);
int p4 = nxt(nxt(nxt(p1, n), n), n);
for (int p3 = nxt(p2, n); p3 != p1; p3 = nxt(p3, n)) {
while (nxt(p2, n) != p3 && Cross(p[p2] - p[p1], p[p3] - p[p1]) < Cross(p[nxt(p2, n)] - p[p1], p[p3] - p[p1])) p2 = nxt(p2, n);
if (p4 == p3) p4 = nxt(p4, n);
while (nxt(p4, n) != p1 && Cross(p[p3] - p[p1], p[p4] - p[p1]) < Cross(p[p3] - p[p1], p[nxt(p4, n)] - p[p1])) p4 = nxt(p4, n);
res = max(res, Cross(p[p2] - p[p1], p[p3] - p[p1]) + Cross(p[p3] - p[p1], p[p4] - p[p1]));
}
}
return res;
}
Point p[N], q[N];
int main() {
// FILE* _INPUT = freopen("input.txt", "r", stdin);
//FILE* _OUTPUT = freopen("output.txt", "w", stdout);
int T; rd(T);
while (T--) {
int n; rd(n);
for (int i = 0; i < n; ++i) {
rd(p[i].x); rd(p[i].y);
}
int tmp = n;
n = Andrew(p, n, q);
if (n == 2) {
puts("0");
continue;
}
else if (n == 3) {
ll ans = Cross(q[1] - q[0], q[2] - q[0]);
ll res = 4e18;
int cnt0 = 0, cnt1 = 0, cnt2 = 0;
for (int i = 0; i < tmp; ++i) {
if (p[i] == q[0]) {
++cnt0;
continue;
}
if (p[i] == q[1]) {
++cnt1;
continue;
}
if (p[i] == q[2]) {
++cnt2;
continue;
}
for (int j = 0; j < 3; ++j) {
ll tmp = Cross(q[nxt(j, n)] - q[j], p[i] - q[j]);
if (tmp < 0) continue;
res = min(res, tmp);
}
}
if (cnt0 > 1 || cnt1 > 1 || cnt2 > 1) res = 0;
ans -= res;
if (ans & 1) printf("%lld.5", ans / 2);
else printf("%lld\n", ans / 2);
}
else {
ll ans = RotateCaliper(q, n);
if (ans & 1) printf("%lld.5\n", ans / 2);
else printf("%lld\n", ans / 2);
}
}
return 0;
}