bzoj 3707. 圈地(平面任意三点构成三角形的最小面积:旋转坐标轴)

Description

2维平面上有n个木桩,黄学长有一次圈地的机会并得到圈到的土地,为了体现他的高风亮节,他要使他圈到的土地面积尽量小。圈地需要圈一个至少3个点的多边形,多边形的顶点就是一个木桩,圈得的土地就是这个多边形内部的土地。(因为黄学长非常的神,所以他允许圈出的第n点共线,那样面积算0)

Input

第一行一个整数n,表示木桩个数。
接下来n行,每行2个整数表示一个木桩的坐标,坐标两两不同。

Output

仅一行,表示最小圈得的土地面积,保留2位小数。

Sample Input

3
0 0
0 1
1 0

Sample Output

0.50

Hint

对于100%的数据,n<=1000。

题意:给n个点,问任意三个点构成三角形的最小面积

思路:把所有可以连成的直线按斜率从小到大排序,枚举直线作为三角形底边,用最接近直线的左右两个点更新答案。关键是如何O(1)查到离直线最近的两个点,这里我们先把所有点按x为第一关键字升序,y为第二关键字升序排列,pos[i]表示原来编号为 i 的点现在的编号,id[i]表示现在的编号 i 对应之前的哪个点,在枚举相邻两条直线l1、l2前后,和l2距离的相对顺序改变的只有l1的两个端点,因为如果有别的点对相对顺序改变,这条直线的斜率一定介于l1、l2之间,矛盾。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 + 7;
const int M = 1e6 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;

struct Point {
    double x, y;
    int i, j;
    Point() { x = y = 0; }
    Point(double a, double b) { x = a, y = b; }
    Point(double a, double b, Point p) { i = a, j = b, x = p.x, y = p.y; }
    Point operator - (const Point &n) const {
        return Point(x - n.x, y - n.y);
    }
    bool operator < (const Point &n) const {
        return x == n.x ? y < n.y : x < n.x;
    }
} p[N];

struct Line {
    int s, t;
    double k;
    bool operator < (const Line &a) const {
        return k < a.k;
    }
} l[M];

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

double cal(Point P, Line L) {
    return 0.5 * fabs(cross((p[L.s] - P), (p[L.t] - P)));
}

int pos[N], id[N];

int main() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%lf%lf", &p[i].x, &p[i].y);
    sort(p + 1, p + n + 1);
    for(int i = 1; i <= n; ++i) pos[i] = id[i] = i;
    int tot = 0;
    for(int i = 1; i <= n; ++i) {
        for(int j = i + 1; j <= n; ++j) {
            double k;
            if(p[i].x == p[j].x) k = 1.0 * inf;
            else k = 1.0 * (p[i].y - p[j].y) / (p[i].x - p[j].x);
            l[++tot] = Line{i, j, k};
        }
    }
    sort(l + 1, l + tot + 1);
    double ans = 1.0 * inf;
    for(int i = 1; i <= tot; ++i) {
        int x = pos[l[i].s], y = pos[l[i].t];
        if(x > y) swap(x, y);
        if(x > 1) ans = min(ans, cal(p[id[x - 1]], l[i]));
        if(y < n) ans = min(ans, cal(p[id[y + 1]], l[i]));
        swap(pos[l[i].s], pos[l[i].t]);
        swap(id[x], id[y]);
    }
    printf("%.2f\n", ans);
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值