Largest Quadrilateral - convex hull + rotating card shell

Title

The meaning of the title of this question is obvious. Given multiple points on a two-dimensional plane, find the maximum area of ​​the quadrilateral that can be formed by these points.

  • The first thing to be done is to find the convex hull of these points. There is the largest quadrilateral area in the quadrilaterals constructed on the convex hull.
  • After forming the convex hull, there are the following three situations
  • The final convex hull formed is a straight line, that is, all points are on a straight line. The number of points of the convex hull formed at this time is 2, and the area of ​​the formed quadrilateral is 0.
  • The final convex hull has only three points, and what is to be constructed at this time is a concave quadrilateral. Then directly enumerate the points in the convex hull, and find the minimum area of ​​this point and the points on two of the convex hulls. Finally, subtract the minimum triangle area from the area of ​​the convex hull triangle.
  • The number of convex hull points formed ≥ 4 ≥ 44 , you need to use the rotating card housing to calculate the area. First divide the quadrilateral into two triangles, then enumerate the first and third points, and use the rotating card to locate the largest triangle area in the current state.
  • The data of this question has pits and repeated points. When there are only three points corresponding to the convex hull, the repeated points can directly form the largest convex hull triangle area.
  • See code for details
#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;
}

Guess you like

Origin blog.csdn.net/bloom_er/article/details/109952765