题意:给定n个点,任选x个点作为节点,若构成凸多边形且该多边形内没有点(不包括边界),则该凸多边形合法,求面积最大的合法凸多边形,输出其面积值
思路:遍历所有点,以任一点Px作为凸多边形左下角(移至坐标原点),逆时针扫该点所有右上角的点对(Pi,Pj),遍历所有点通过叉积判断PxPiPj构成的三角形中是否有点,若无则合法,再判断逆时针排序Pj的上一个点是否与其共线,若共线,则上一个三角形的边界上有点,那么不能和当前三角形合并,否则多边形内有点;若不共线,则可将本三角形合并至前面扫过的合法多边形中,dp[i][j]为最后遍历的三角形为PxPiPj的多边形的最大面积,Pk为Pi的任一前节点,s为PxPiPj三角形面积,若<0(即边长顺时针旋转),则满足凸包,状态转移方程dp[i][j] = max(dp[i][j],dp[k][i]+s)
代码:
/*#include <bits/stdc++.h>*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const ll mod = 2147493647;
const int maxn = 55;
#define INF 0xffffff
typedef struct Point
{
double x, y;
Point(double a = 0, double b = 0)
{
x = a, y = b;
}
const Point operator -(const Point &b)const
{
return Point(x - b.x, y - b.y);
}
double operator^(const Point &b)const//叉积
{
return x*b.y - y*b.x;
}
double l()//向量长度
{
return x*x+y*y;
}
}Point;
int n;
Point p[maxn],se[maxn];
double dp[maxn][maxn];
int cnt;
double ans = 0;
bool cmp1(Point a, Point b)//按照左下角排序
{
if(a.y==b.y)return a.x<b.x;
return a.y<b.y;
}
bool cmp2(Point a, Point b)//顺时针排序,若共线先取离原点近的点
{
double tmp = a^b;
if(tmp==0)
{
return a.l()<b.l();
}
return tmp<0;
}
void sol()
{
for(int i=1;i<cnt;i++)
{
for(int j = i+1;j<cnt;j++)
{
if((se[i]^se[j])==0)continue;//若点对共线则无三角形构成
int f = 1;
for(int k=1;k<cnt;k++)
{
Point tmp(-se[i].x,-se[i].y);
if((se[i]^se[k])*(se[j]^se[k])<0 && (tmp^(se[k]-se[i]))*((se[j] - se[i])^(se[k] - se[i]))<0)//利用叉积判断点是否在三角形内
{
f = 0;
break;
}
}
if(f)
{
double s = (se[j]^se[i]);
dp[i][j] = s;
if((se[i-1]^se[i])!=0)//若前一个点与当前i点不共线,则可以保证合并后多边形内无节点
{
for(int k=0;k<i;k++)
{
if(((se[i] - se[k])^(se[j] - se[k]))>0)continue;
dp[i][j] = max(dp[i][j],dp[k][i]+s);
}
}
ans = max(ans,dp[i][j]);
}
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
ans = 0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
}
sort(p,p+n,cmp1);//从左下角点开始逆时针排序
for(int i=0;i<n-2;i++)
{
cnt = 0;
memset(dp,0,sizeof(dp));
for(int j=i;j<n;j++)
{
se[cnt++] = p[j] - p[i];
}
sort(se,se+cnt,cmp2);
sol();
}
printf("%.1lf\n",ans/2.0);
}
}