O(n^3)求最大空凸包模板(2017沈阳icpc-C Empty Convex Polygons )

题目链接:https://vjudge.net/contest/358714#problem/C

题意:求最大空凸包的面积,点的个数n<=50。

思路:

  参考链接:https://blog.csdn.net/cdsszjj/article/details/79366813

  计算几何+DP。

  首先枚举凸包最左下角的点O,忽略O下面的点,对其它点进行极角排序。

  然后枚举凸包上的最后一个点i,用dp[i][j]表示以三角形Oij为凸包的最后一块三角形的最大空凸包的面积。那么可以得到转移方程:

  dp[i][j] = max ( dp[i][j] , S(Oij) + dp[j][k] ) (其中三角形Oij内无顶点,边Oi上无顶点,k点在ij的右边)

  (如果Oi上有顶点,Oi就只能是凸包的边,那么它不能更新dp[i][j],比如后面的dp[a][i]可以用S(Oai)+dp[i][j]来更新,此时Oi上的点就会成为凸包内部的点。所以此时只能用来更新ans,而不能更新dp数组。相反如果Oi上没有点,那么它可以更新ans和dp数组)

  

  上述的方法复杂度是O(n^4),而且没有说怎么遍历合法的j。

  后面讲怎么优化这个DP。

  对于每个i,令j1 = i-1。显然如果Oj1与Oi不共线的话这个j1将是第一个合法的j,如果共线的话,就找最大的不共线的作为j1,并且此时由于Oi上有点,不能将结果更新dp数组。

  显然第二个满足条件的j2就是最大的在ij1右边的点,j3就是最大的在ij2右边的点......

  这里我们用g[i][j] 表示 max ( dp[i][k] ) ,1<=k<=j。并且把jn作为凸包的最后一个点时,jn+1就是第一个符合条件的j,然后就可以在O(1)内更新dp[i][j]了:dp[i][j] = S(Oij) + g[j][k]。

  枚举O点为O(n),枚举i为O(n),枚举j为O(n),计算dp[i][j]为O(1),计算g是和枚举j并列的,为O(n)。因此总复杂度为O(n^3)。(另:代码中没有g数组,直接利用dp数组,因为得到dp数组之后就用不到了)

 

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

const int maxn=55;
const double PI=acos(-1.0);

struct Point{
    double x,y;
    Point():x(0),y(0){}
    Point(double x,double y):x(x),y(y){}
}a[maxn],p[maxn],O;

//计算叉积p0p1×p0p2
double cross(Point p0,Point p1,Point p2){
    return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
//计算p1p2的距离
double dis(Point p1,Point p2){
    return sqrt((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
//极角排序函数,角度相同则距离小的在前面
bool cmp(Point p1,Point p2){
    double tmp=cross(O,p1,p2);
    if(tmp>0) return true;
    else if(tmp==0&&dis(O,p1)<dis(O,p2)) return true;
    else return false;
}

int T,n,cnt;
double ans,dp[maxn][maxn];

void solve(){
    for(int i=0;i<n;++i)
        for(int j=0;j<n;++j)
            dp[i][j]=0.0;
    for(int i=1;i<=cnt;++i){
        int j=i-1;
        while(j&&!cross(O,p[i],p[j])) --j;
        int flag=(j==i-1);
        while(j){
            int k=j-1;
            while(k&&cross(p[i],p[j],p[k])>0) --k;
            double area=fabs(cross(O,p[i],p[j]))/2.0;
            if(k) area+=dp[j][k];
            if(flag) dp[i][j]=area;
            ans=max(ans,area);
            j=k;
        }
        if(flag) for(int j=1;j<i;++j) dp[i][j]=max(dp[i][j],dp[i][j-1]);
    }
}

int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        ans=0.0;
        for(int i=0;i<n;++i){
            int x,y;
            scanf("%d%d",&x,&y);
            a[i].x=x,a[i].y=y;
        }
        for(int i=0;i<n;++i){
            O=a[i];
            cnt=0;
            for(int j=0;j<n;++j)
                if(a[j].y>a[i].y||a[j].y==a[i].y&&a[j].x>a[i].x) p[++cnt]=a[j];
            sort(p+1,p+cnt+1,cmp);
            solve();
        }
        printf("%.1f\n",ans);
    }
    return 0;
}

 

posted @ 2020-02-26 13:50  Frank__Chen  阅读(457)  评论(0编辑  收藏  举报