2017ACM/ICPC亚洲区沈阳站-HDU 6219(最大空凸包--几何叉积+DP)

题意:给定n个点,任选x个点作为节点,若构成凸多边形且该多边形内没有点(不包括边界),则该凸多边形合法,求面积最大的合法凸多边形,输出其面积值

思路:遍历所有点,以任一点Px作为凸多边形左下角(移至坐标原点),逆时针扫该点所有右上角的点对(Pi,Pj),遍历所有点通过叉积判断PxPiPj构成的三角形中是否有点,若无则合法,再判断逆时针排序Pj的上一个点是否与其共线,若共线,则上一个三角形的边界上有点,那么不能和当前三角形合并,否则多边形内有点;若不共线,则可将本三角形合并至前面扫过的合法多边形中,dp[i][j]为最后遍历的三角形为PxPiPj的多边形的最大面积,Pk为Pi的任一前节点,s为PxPiPj三角形面积,若\underset{PkPi}{\rightarrow}\times\underset{PiPj}{\rightarrow}<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);
    }
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值