BZOJ1069 [SCOI2007] 最大土地面积

@(BZOJ)[凸包, 旋轉卡殼]

Description

在某块平面土地上有\(N\)个点,你可以选择其中的任意四个点,将这片土地围起来,当然,你希望这四个点围成
的多边形面积最大。

Input

第1行一个正整数\(N\),接下来\(N\)行,每行\(2\)个数\(x\),\(y\),表示该点的横坐标和纵坐标。

Output

最大的多边形面积,答案精确到小数点后\(3\)位。

Sample Input

5
0 0
1 0
1 1
0 1
0.5 0.5

Sample Output

1.000

HINT

数据范围 \(n<=2000\), $ |x|, |y|<=100000$

Solution

模板題
主要用於學習Graham掃描法求凸包, 叉積判斷向量的關係以及叉積計算三角形面積.
至於所謂的旋轉卡殼做法, 在這一題上實際上體現得並不深刻. 可以作為一個初步的理解吧.

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

const int N = 1 << 11;
const double INF = 1e30;

int n;

struct point
{
    double x, y;
    
    point(){}
    
    inline point(double _x, double _y)
    {
        x = _x, y = _y;
    }
    
    inline friend int operator <(const point &a, const point &b)
    {
        return a.x == b.x ? a.y < b.y : a.x < a.y;
    }
    
    inline friend point operator -(const point &a, point &b)
    {
        return point(a.x - b.x, a.y - b.y);
    }
}a[N];

point orig;

inline double det(point a, point b)
{
    return a.x * b.y - a.y * b.x;
}

inline int cmp(point a, point b)
{
    return det(a - orig, b - orig) > 0;
}

int top;
point stack[N];

inline void graham()    //求凸包
{
    int p = 0;
    
    for(int i = 1; i < n; i ++)
        if(a[i] < a[p])
            p = i;

    swap(a[0], a[p]);
    orig = a[0];
    sort(a + 1, a + n, cmp);
    
    top = 0;
    stack[top ++] = a[0], stack[top ++] = a[1];
    
    for(int i = 2; i < n; i ++)
    {
        while(top && det(stack[top - 1] - stack[top - 2], a[i] - stack[top - 1]) <= 0)
            top --;
            
        stack[top ++] = a[i];
    }
}

inline double getArea(point a, point b, point c)
{
    return fabs(det(a - c, b - c) / 2);
}

inline double solve()   //旋轉卡殼, 利用的是凸包的單調性.
{
    double ans = - INF;
    
    for(int i = 0; i < top; i ++) //枚舉一條對邊
    {
        int a = i % top, b = (i + 2) % top; //發現另一條對邊的端點具有單調性
        
        for(int j = i + 2; j < top; j ++)
        {
            while((a + 1) % top != j && getArea(stack[i], stack[j], stack[a]) < getArea(stack[i], stack[j], stack[(a + 1) % top]))
                a = (a + 1) % top;
                 
            while((b + 1) % top != i && getArea(stack[i], stack[j], stack[b]) < getArea(stack[i], stack[j], stack[(b + 1) % top]))
                b = (b + 1) % top;
                
            ans = max(ans, getArea(stack[i], stack[j], stack[a]) + getArea(stack[i], stack[j], stack[b]));
        }
    }
    
    return ans;
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("BZOJ1069.in", "r", stdin);
    freopen("BZOJ1069.out", "w", stdout);
    #endif
    
    scanf("%d", &n);
    
    for(int i = 0; i < n; i ++)
        scanf("%lf%lf", &a[i].x, &a[i].y);
        
    graham();
    printf("%.3lf", solve());
}

转载于:https://www.cnblogs.com/ZeonfaiHo/p/6430714.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值