题目描述
已知平面上 $n$ 个点,称点集 $S$ 是内凸包,当且仅当:
- $S$ 是某个点集的凸包;
- 设 $S$ 构成的凸多边形为 $G$,则 $S$ 以外的点要么在 $G$ 的边上,要么在 $G$ 外.
试最大化内凸包构成的凸多边形的面积。
题解
首先我们考虑枚举点 $O$ 作为凸包最下方的点,然后取出它上方的点按极角排序。因为我们是按照极角顺序取点的,所以我们可以设计 $ ext{dp}$ : $f[i][j]$ 表示凸包最后一个点为 $i$ ,其次为 $j$ 的最大面积,我们可以得到转移式: $f[i][j]=max{f[j][k]+S(O,i,j)}$ ,其中 $k<j$ , $Oij$ 中没有其它点且 $k$ 在 $(i,j)$ 下方。这样是 $O(n^4)$ 的,过不去。
考虑优化,我们可以发现对于 $i$ 来说,从 $Oi$ 开始,找到极角最大的 $j$ 在 $Oi$ 下方,然后将 $O$ 设为 $i$ , $i$ 设为 $j$ 接着找,这些点才会满足 $Oij$ 内没有别的点,然后我们设 $g[i][j]=max{f[i][k]}$ ,其中 $k<j$ 且 $k$ 是那些点中的一个,所以对于找到的点 $j$ , $f[i][j]=S(O,i,j)+g[j][next[j]]$ 。这样做就是 $O(n^3)$ 的了。
主要是考察凸包的构建过程,不应该没思路。
代码
#include <bits/stdc++.h> 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 ",1.*s/2); } int main(){for (cin>>T;T--;work());return 0;}