#4719. 内凸包

题目描述

已知平面上 $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

考虑优化,我们可以发现对于 $i$ 来说,从 $Oi$ 开始,找到极角最大的 $j$ 在 $Oi$ 下方,然后将 $O$ 设为 $i$ , $i$ 设为 $j$ 接着找,这些点才会满足 $Oij$ 内没有别的点,然后我们设 $g[i][j]=max\{f[i][k]\}$ ,其中 $k

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

代码

#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. 内凸包)