[BZOJ1069]-最大土地面积-凸包+旋转卡壳

题目

BZOJ1069传送门

题面

在某块平面土地上有N个点,你可以选择其中的任意四个点,将这片土地围起来,当然,你希望这四个点围成的多边形面积最大。
数据范围 n<=2000, |x|,|y|<=100000

输入输出格式

输入格式:
第1行一个正整数N,接下来N行,每行2个数x,y,表示该点的横坐标和纵坐标。

输出格式:
最大的多边形面积,答案精确到小数点后3位。


解法

直接讲做法吧
很容易脑补出来,如果要面积最大,四边形的顶点一定都在凸包上
(不要问me凸包只有三个点的情况…me也不是很清楚,不过貌似没有这样的数据)
用单调栈维护出凸包之后,在凸包上枚举两个点的连线作为四边形的对角线,然后在对角线两边分别找面积最大三角形
很明显在固定两个点之后,另外两个点也是有单调性的,时间复杂度 Θ(N2)

关于最大四边形四点一定在凸包上的正确性,如果有一个点在凸包内,那么< 对角线-当前顶点 >三角形的面积一定是比< 对角线-凸包顶点 >三角形的面积小的,画个图就会明白了


自带大常数的代码

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

const double eps = 1e-13 ;
int N , topp ;
struct Vector{
    double x , y ;
    Vector(){} ;
    Vector( double x_ , double y_ ):
        x(x_) , y(y_){} ;
    double len(){ return sqrt( x * x + y * y ) ; }
} ;
typedef Vector Point ;
Point a[2005] , poly[2005] ;

int dcmp( double x ){
    if( x < eps && x > -eps ) return 0 ;
    return x > eps ? 1 : -1 ;
}

Vector operator + ( const Vector &A , const Vector &B ) { return Vector( A.x+B.x , A.y+B.y ) ; }
Vector operator - ( const Vector &A , const Vector &B ) { return Vector( A.x-B.x , A.y-B.y ) ; }
Vector operator * ( const Vector &A , const double &p ) { return Vector( A.x*p   , A.y*p   ) ; }
Vector operator / ( const Vector &A , const double &p ) { return Vector( A.x/p   , A.y/p   ) ; }
bool operator  == ( const Vector &A , const Vector &B ) { return ( A.x == B.x && A.y == B.y ); }
bool operator  <  ( const Point  &A , const Point  &B ) { return A.x < B.x || ( A.x == B.x && A.y < B.y ) ; }

double fabs ( double x ){ return dcmp(x) < 0 ? -x : x ; }
double Dot  ( const Vector &A , const Vector &B ){ return A.x * B.x + A.y * B.y ; }
double Cross( const Vector &A , const Vector &B ){ return A.x * B.y - A.y * B.x ; }

void convex( Point *a , int siz , Point *p , int &topp ){
    sort( a + 1 , a + siz + 1 ) ;
    topp = 0 ;
    for( int i = 1 ; i <= siz ; i ++ ){
        while( topp >= 2 && Cross( a[i] - p[topp-1] , p[topp] - p[topp-1] ) >= 0 ) topp -- ;
        p[++topp] = a[i] ;
    }
    int las = topp ;
    for( int i = siz - 1 ; i >= 1 ; i -- ){
        while( topp - las >= 1 && Cross( a[i] - p[topp-1] , p[topp] - p[topp-1] ) >= 0 ) topp -- ;
        p[++topp] = a[i] ;
    }
    topp -- ;
/*  for( int i = 1 ; i <= topp ; i ++ ){
        printf( "poly : [%f %f]\n" , p[i].x , p[i].y )  ;
    }
*/
}

double ans ;
void RC(){
    poly[topp+1] = poly[1] ;
    for( int i = 1 ; i <= topp ; i ++ ){
        int dn = i+1 , up = (i+2)%topp+1 ;
        for( int j = i + 2 ; j <= topp ; j ++ ){
            while( fabs( Cross( poly[dn] - poly[i] , poly[j] - poly[i] ) ) <
                   fabs( Cross( poly[dn+1] - poly[i] , poly[j] - poly[i] ) ) ) dn ++ ;

            while( fabs( Cross( poly[j] - poly[i] , poly[up] - poly[i] ) ) <
                   fabs( Cross( poly[j] - poly[i] , poly[up+1] - poly[i] ) ) ) up = up %topp + 1 ;

            ans = max( ans , fabs( Cross( poly[up] - poly[i] , poly[j] - poly[i] ) ) +
                             fabs( Cross( poly[j] - poly[i] , poly[dn] - poly[i] ) ) ) ;
        }
    }
    printf( "%.3f" , ans/2 ) ;
}

int main(){
    scanf( "%d" , &N ) ;
    for( int i = 1 ; i <= N ; i ++ )
        scanf( "%lf%lf" , &a[i].x , &a[i].y ) ;
    convex( a , N , poly , topp ) ;
    RC() ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值