Largest Quadrilateral --凸包+旋转卡壳

题意

这题的题目意思很显然,给出多个在二维平面的点,求这些点中所能形成的四边形的最大面积

  • 首先要进行的是求这些点的凸包,在凸包上所构建的四边形中存在最大的四边形面积
  • 在形成凸包后,有下列三种情况
  • 最终所形成的凸包是一条直线,即所有点都在一条直线上,此时形成的凸包的点的个数为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;
}

猜你喜欢

转载自blog.csdn.net/bloom_er/article/details/109952765
今日推荐