JZOJ1157. [GDOI2004]可怜的绵羊

题目大意

给出一个\(n\)个点的凸包(顶点按照逆时针排序), 并另外给出\(m\)个点(可能在凸包内或外, 但不会在凸包顶点上), 询问用原凸包中的点围成的不包括任一所给点的最大凸包面积.
\(n,m<=100\)

吐槽环节

出题人 手 12.5分.jpg
可能是做过的第一道计算几何的题目.

前置知识:向量叉积

设两个向量\(\boldsymbol a, \boldsymbol b\), 定义向量叉积\(\boldsymbol{a} \times \boldsymbol{b}=\boldsymbol{c}\), 所得的结果是一个向量.

运算结果\(\boldsymbol c\)满足:

  1. \(|\boldsymbol c| = |\boldsymbol a||\boldsymbol b|\sin\theta\), 其中\(\theta\)是向量\(\boldsymbol a, \boldsymbol b\)的夹角.由此可得\(|\boldsymbol c|\)等于以\(\boldsymbol a, \boldsymbol b\)为邻边的平行四边形的面积.(所以叉积的模长也经常被用来计算三角形的面积)

  2. \(\boldsymbol c\)的方向是垂直于\(\boldsymbol a, \boldsymbol b\)所在平面的. 具体是正方向还是负方向, 遵循右手定则
    (右手四指指向\(\boldsymbol a\)的方向, 向手心偏折后指向\(\boldsymbol b\)的方向, 那么拇指所指的方向就是\(\boldsymbol c\)的方向.)
    (所以叉积的模长的正负性也经常被用来判断两个向量的相对方向.)

解题思路

我们把不能包括的点称作「危险点」 则在原凸包外的危险点显然是没有用的. 先除去这些点.

由于我们最后弄出来的凸包要由原凸包里的点构成, 所以可以把新凸包看做由几个连续的三角形组成, 且拥有一个共同顶点.

所以我们可以枚举这一个共同顶点, 然后枚举两个顶点表示三角形的起始点和终止点来计算答案. 只需判断这中间有没有危险点即可.

如何判断? 我们对于每一条连线, 计算其「外部」, 也就是顺时针方向(其实逆时针方向也可以)的危险点个数(包括在这条线上的). 最后, 如果三角形三条边的外部的点的总数即为实际凸包内点的总个数, 那么这个三角形就合法了.

时间复杂度\(O(n^3)\)(\(n,m\)同阶).

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 110
#define ll long long
#define init(a, b) memset(a, b, sizeof(a))
#define fo(i, a, b) for(int i = (a); i <= (b); ++i)
#define fd(i, a, b) for(int i = (a); i >= (b); --i)
using namespace std;
inline int read() // notice : 1. long long ? 2. negative ?
{
	int x = 0; char ch = getchar(); bool ne = 0;
	while(ch < '0' || ch > '9')	ne |= (ch == '-'), ch = getchar();
	while(ch >= '0' && ch <= '9')	x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return ne ? -x : x;
}
int n, m, nm, cnt[N][N];
struct P
{
	ll x, y;
	P(){}
	P(int _x, int _y){x = _x, y = _y;}
	friend ll operator*(const P a, const P b)
	{
		return a.x * b.y - a.y * b.x;
	}
	friend P operator-(const P a, const P b)
	{
		return P(a.x - b.x, a.y - b.y);
	}
}a[N], b[N];
ll ans, f[N];
bool in[N];
int main()
{
//	freopen("sheep.in", "r", stdin);
//	freopen("sheep.out", "w", stdout);
	n = read();
	fo(i, 1, n)	a[i].x = read(), a[i].y = read();
	m = read();
	fo(i, 1, m)
	{
		b[i].x = read(), b[i].y = read(), in[i] = 1;
		fo(j, 1, n)
			if((a[j % n + 1] - a[j]) * (b[i] - a[j]) < 0)
			{
				in[i] = 0; break;
			}
		nm += in[i];
	}
	fo(i, 1, n)	fo(j, 1, n)	if(i != j)
		fo(k, 1, m)	if(in[k])
		{
			cnt[i][j] += ((a[j] - a[i]) * (b[k] - a[i]) <= 0);
//			printf("%lld\n", (a[j] - a[i]) * (b[k] - a[i]));
		}
	fo(i, 1, n - 2)
	{
		init(f, 0);
		fo(j, i + 1, n - 1)	fo(k, j + 1, n)
			if(cnt[i][j] + cnt[j][k] + cnt[k][i] == nm)
				f[k] = max(f[k], f[j] + (a[j] - a[i]) * (a[k] - a[i]));
		fo(j, i + 2, n)	ans = max(ans, f[j]);
	}
	ans ? printf("%.2lf", ans / 2.0) : printf("die");
	return 0;
}
posted @ 2021-04-03 16:53  Martin_MHT  阅读(93)  评论(0)    收藏  举报