poj2079 旋转卡壳

题目

题目给出一个点集,要求其中选三个点能构成的最大三角形的面积。

我的方法是O(n^2),似乎能通过二分降成nlogn,不过好在这题数据不大可以过。

可以证明这三个点必在凸包点中,如果说存在一个面积更大的三角形而它有的点不在凸点上,那么必定可以在凸点上找到一点,使得(替换后)三角形面积更大。

我们先求出凸包,然后在凸点上寻找这三个点即可。寻找过程运用了旋转卡壳思想,固定一条边的跨度,然后寻找最高的顶点。

#include<iostream>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=5e4+2;
const double eps=1e-8;
int n;
inline int sgn(double x){
    if(fabs(x)<eps) return 0;
    else if(x<0) return -1;
    else return 1;
}

struct Point{
    double x,y;
    Point(){
        x=0;
        y=0;
    }
    Point(double a,double b):x(a),y(b){}
    inline Point operator-(const Point& b)const{
        return Point(x-b.x,y-b.y);
    }
    inline Point operator+(const Point& b)const{
        return Point(x+b.x,y+b.y);
    }
    inline double dot(const Point& b)const{
        return x*b.x+y*b.y;
    }
    inline double cross(const Point&b,const Point& c)const{
        return (b.x-x)*(c.y-y)-(c.x-x)*(b.y-y);
    }
    double operator^(Point a){return x*a.y-y*a.x;}
    double operator*(Point a){return x*a.x+y*a.y;}
};

double dist(const Point& a, const Point& b){
    return sqrt((b-a).dot(b-a));
}
Point stk[maxn];
Point ps[maxn];
bool operator<(const Point& a,const Point& b){
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
int Andrew(){	//逆时针扫描
    sort(ps,ps+n);
    int top=0;
    for(int i=0;i<n;i++){
        while(top>1&&sgn((stk[top-1]-stk[top-2])^(ps[i]-stk[top-1]))<=0) top--;
        stk[top++]=ps[i];
    }
    int k=top;
    for(int i=n-2;i>=0;i--){
        while(top>k&&sgn((stk[top-1]-stk[top-2])^(ps[i]-stk[top-1]))<=0) top--;
        stk[top++]=ps[i];
    }
    return top;	//凸包点数
}//结果点集逆时针存放在stk中

double rotating_caliper(Point* ps){
    double res=0;
    int k;
    for(int l=1;l<=n/2;l++){    //基边的跨度
        k=l+1;                  //决定三角形高的顶点
        for(int i=0;i<n;i++){
            while(ps[i].cross(ps[(i+l)%n],ps[k%n])<ps[i].cross(ps[(i+l)%n],ps[(k+1)%n]))
                k=(k+1)%n;
            res=max(res,ps[i].cross(ps[(i+l)%n],ps[k%n])/2.0);
        }
    }
    return res;
}

int main(){
    while(~scanf("%d",&n)&&(~n)){
        for(int i=0;i<n;i++){
            scanf("%lf%lf",&ps[i].x,&ps[i].y);
        }
        n=Andrew();
        double ans=rotating_caliper(stk);
        printf("%.2f\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Absoler

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值