题目链接: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; }
原文地址:https://www.cnblogs.com/FrankChen831X/p/12366674.html