JAG2016 (凸包)

题目连接:点击这里

题意:给出n个点,用一条 x=i 将点分成 i >i ,求两边凸包面积和的最小值。

先把点按照x,y值排序,然后从左往右维护一个上凸壳和下凸壳的面积即可。因为有同一竖线上的多点情况,所以在计算某条竖线左侧的凸包面积的时候还要加上由竖线最上面的点,竖线最下面的点和最左边的起点构成的三角形的面积。从右往左维护也类似的维护凸壳面积。

trick:用叉积计算面积的两倍来避免精度误差;最大值在极限情况下接近4e8,所以设INF的时候要小心.

#include<bits/stdc++.h>
using namespace std;
#define maxn 100005

struct Point {
    long long x, y;
    Point (long long _x = 0, long long _y = 0) {
        x = _x, y = _y;
    }
    Point operator + (const Point &a) const {
        return Point (x+a.x, y+a.y);
    }
    Point operator - (const Point &a) const {
        return Point (x-a.x, y-a.y);
    }
    bool operator < (const Point &a) const {
        return x < a.x || (x == a.x && y < a.y);
    }
}p[maxn];
int n;

long long a[maxn], b[maxn], c[maxn], d[maxn];

long long cross (Point a, Point b) {
    return a.x*b.y - a.y*b.x;
}

int g1[maxn], m1;
int g2[maxn], m2;

long long solve () {
    m1 = m2 = 0;
    a[1] = b[1] = 0;
    g1[m1++] = 1;
    g2[m2++] = 1;
    int i = 1, j;
    while (i+1 <= n && p[i+1].x == p[1].x) {
        i++;
        a[i] = 0;
    }
    g1[m1++] = i;
    for (i++; i <= n; i = j+1) {
        j = i;
        while (j+1 <= n && p[j+1].x == p[i].x) j++;
        while (m1 > 1 && cross (p[j]-p[g1[m1-1]], p[g1[m1-1]]-p[g1[m1-2]]) <= 0) m1--;
        while (m2 > 1 && cross (p[g2[m2-1]]-p[g2[m2-2]], p[i]-p[g2[m2-1]]) <= 0) m2--;
        a[j] = a[g1[m1-1]] + cross (p[j]-p[1], p[g1[m1-1]]-p[1]);
        b[i] = b[g2[m2-1]] + cross (p[g2[m2-1]]-p[1], p[i]-p[1]);
        g1[m1++] = j, g2[m2++] = i;
    }


    m1 = m2 = 0;
    c[n] = d[n] = 0;
    g1[m1++] = n, g2[m2++] = n;
    i = n;
    while (i-1 >= 1 && p[i].x == p[i-1].x) {
        i--;
        d[i] = 0;
    }
    g2[m2++] = i;
    for (i--; i >= 1; i = j-1) {
        j = i;
        while (j >= 1 && p[j-1].x == p[i].x) j--;
        while (m1 > 1 && cross (p[g1[m1-1]]-p[g1[m1-2]], p[i]-p[g1[m1-1]]) <= 0) m1--;
        while (m2 > 1 && cross (p[j]-p[g2[m2-1]], p[g2[m2-1]]-p[g2[m2-2]]) <= 0) m2--;
        c[i] = c[g1[m1-1]] + cross (p[g1[m1-1]]-p[n], p[i]-p[n]);
        d[j] = d[g2[m2-1]] + cross (p[j]-p[n], p[g2[m2-1]]-p[n]);
        g1[m1++] = i;
        g2[m2++] = j;
    }

    long long ans = 8e18;
    int k;
    for (i = 1; i <= n; i = j+1) {
        j = i;
        while (j+1 <= n && p[j+1].x == p[i].x) j++;
        long long tmp1 = a[j]+b[i]+cross (p[i]-p[1], p[j]-p[1]);
        k = j+1;
        long long tmp2;
        if (k > n) {
            tmp2 = 0;
        }
        else {
            while (k+1 <= n && p[k+1].x == p[j+1].x) k++;
            tmp2 = c[k]+d[j+1]+cross (p[k]-p[n], p[j+1]-p[n]);
        }
        ans = min (ans, tmp1+tmp2);
    }
    return ans;
}

int main () {
    //freopen ("1.txt", "r", stdin);
    scanf ("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf ("%lld%lld", &p[i].x, &p[i].y);
    }
    sort (p+1, p+1+n);
    long long ans = solve ();
    cout << ans/2+ans%2 << endl;
    return 0;
}
/*
10
-1000000000 1000000000
-500000000 1000000000
0 1000000000
500000000 1000000000
1000000000 1000000000
-1000000000 -1000000000
-500000000 -1000000000
0 -1000000000
500000000 -1000000000
1000000000 -1000000000

*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值