#4719. 内凸包

题目描述

已知平面上 n n n 个点,称点集 S S S 是内凸包,当且仅当:

  • S S S 是某个点集的凸包;
  • S S S 构成的凸多边形为 G G G,则 S S S 以外的点要么在 G G G 的边上,要么在 G G G 外.

试最大化内凸包构成的凸多边形的面积。

题解

首先我们考虑枚举点 O O O 作为凸包最下方的点,然后取出它上方的点按极角排序。因为我们是按照极角顺序取点的,所以我们可以设计 dp \text{dp} dp f [ i ] [ j ] f[i][j] f[i][j] 表示凸包最后一个点为 i i i ,其次为 j j j 的最大面积,我们可以得到转移式: f [ i ] [ j ] = m a x { f [ j ] [ k ] + S ( O , i , j ) } f[i][j]=max\{f[j][k]+S(O,i,j)\} f[i][j]=max{f[j][k]+S(O,i,j)} ,其中 k < j kk<j O i j Oij Oij 中没有其它点且 k k k ( i , j ) (i,j) (i,j) 下方。这样是 O ( n 4 ) O(n^4) O(n4) 的,过不去。

考虑优化,我们可以发现对于 i i i 来说,从 O i Oi Oi 开始,找到极角最大的 j j j O i Oi Oi 下方,然后将 O O O 设为 i i i i i i 设为 j j j 接着找,这些点才会满足 O i j Oij Oij 内没有别的点,然后我们设 g [ i ] [ j ] = m a x { f [ i ] [ k ] } g[i][j]=max\{f[i][k]\} g[i][j]=max{f[i][k]} ,其中 k < j kk<j k k k 是那些点中的一个,所以对于找到的点 j j j f [ i ] [ j ] = S ( O , i , j ) + g [ j ] [ n e x t [ j ] ] f[i][j]=S(O,i,j)+g[j][next[j]] f[i][j]=S(O,i,j)+g[j][next[j]] 。这样做就是 O ( n 3 ) O(n^3) O(n3) 的了。

主要是考察凸包的构建过程,不应该没思路。

代码

#include 
using namespace std;
int T,n,m,s,f[55][55],c[55];
struct O{int x,y;}b[55],a[55];
O operator -(O A,O B){
	return (O){A.x-B.x,A.y-B.y};
}
int operator *(O A,O B){
	return A.x*B.y-A.y*B.x;
}
int S(O A){
	return A.x*A.x+A.y*A.y;
}
bool cmp(O A,O B){
	return A*B<0 || (A*B==0 && S(A)<S(B));
}
void W(){
	memset(f,0,sizeof f);
	sort(a+1,a+m+1,cmp);
	for (int j,k,t,v,i=2;i<=m;i++){
		j=i-1;t=0;
		while(j && a[j]*a[i]==0) j--;
		while(j){
			c[++t]=j;k=j-1;v=a[i]*a[j];
			while(k && (a[k]-a[j])*(a[i]-a[j])<0) k--;
			if (k) v+=f[j][k];
			f[i][j]=v;s=max(v,s);j=k;
		}
		for (int u=t-1;u>0;u--)
			f[i][c[u]]=max(f[i][c[u]],f[i][c[u+1]]);
	}
}
void work(){
	scanf("%d",&n);s=0;
	for (int i=1;i<=n;i++)
		scanf("%d%d",&b[i].x,&b[i].y);
	for (int i=1;i<=n;i++){
		m=0;
		for (int j=1;j<=n;j++){
			if (b[j].y>b[i].y || (b[j].y==b[i].y && b[j].x>b[i].x))
				a[++m]=b[j]-b[i];
		}
		W();
	}
	printf("%.1lf\n",1.*s/2);
}
int main(){for (cin>>T;T--;work());return 0;}

你可能感兴趣的:(#4719. 内凸包)