#4719. 内凸包

时间:2021-11-03 20:47:25

题目描述

已知平面上 $n$ 个点,称点集 $S$ 是内凸包,当且仅当:
- $S$ 是某个点集的凸包;
- 设 $S$ 构成的凸多边形为 $G$,则 $S$ 以外的点要么在 $G$ 的边上,要么在 $G$ 外.

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

题解

首先我们考虑枚举点 $O$ 作为凸包最下方的点,然后取出它上方的点按极角排序。因为我们是按照极角顺序取点的,所以我们可以设计 $text{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("%.1lfn",1.*s/2);
}
int main(){for (cin>>T;T--;work());return 0;}