HDU 3761 (二分 半平面交)

本文介绍了一种解决凸包问题的算法,通过二分查找和半平面交集的方法来找到炸掉最少数量的点使凸包面积归零的方案。文章详细解释了如何通过点、线等基本几何元素的操作实现这一目标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:点击这里

题意:顺时针给出一个凸包, 问最少炸掉多少点使得凸包面积为0.

炸掉的点必然是连续的。 然后二分一下结果, 每次判断半平面的面积。 注意chenk的时候要check面积而不是半平面交的顶点个数。 注意顺时针输入这个trick。

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
#define maxn 100005

const double eps = 1e-8;
const double INF = 1e20;
const double pi = acos (-1.0);

int dcmp (double x) {
    if (fabs (x) < eps) return 0;
    return (x < 0 ? -1 : 1);
}
inline double sqr (double x) {return x*x;}

//*************点
struct Point {
    double x, y;
    Point (double _x = 0, double _y = 0):x(_x), y(_y) {}
    void input () {scanf ("%lf%lf", &x, &y);}
    void output () {printf ("%.2f %.2f\n", x, y);}
    bool operator == (const Point &b) const {
        return (dcmp (x-b.x) == 0 && dcmp (y-b.y) == 0);
    }
    bool operator < (const Point &b) const {
        return (dcmp (x-b.x) == 0 ? dcmp (y-b.y) < 0 : x < b.x);
    }
    Point operator + (const Point &b) const {
        return Point (x+b.x, y+b.y);
    }
    Point operator - (const Point &b) const {
        return Point (x-b.x, y-b.y);
    }
    Point operator * (double a) {
        return Point (x*a, y*a);
    }
    Point operator / (double a) {
        return Point (x/a, y/a);
    }
    double len2 () {//返回长度的平方
        return sqr (x) + sqr (y);
    }
    double len () {//返回长度
        return sqrt (len2 ());
    }
    Point change_len (double r) {//转化为长度为r的向量
        double l = len ();
        if (dcmp (l) == 0) return *this;//零向量返回自身
        r /= l;
        return Point (x*r, y*r);
    }
    Point rotate_left () {//顺时针旋转90度
        return Point (-y, x);
    }
    Point rotate_right () {//逆时针旋转90度
        return Point (y, -x);
    }
    Point rotate (Point p, double ang) {//绕点p逆时针旋转ang
        Point v = (*this)-p;
        double c = cos (ang), s = sin (ang);
        return Point (p.x + v.x*c - v.y*s, p.y + v.x*s + v.y*c);
    }
    Point normal () {//单位法向量
        double l = len ();
        return Point (-y/l, x/l);
    }
};

double cross (Point a, Point b) {//叉积

    return a.x*b.y-a.y*b.x;
}
double dot (Point a, Point b) {//点积

    return a.x*b.x + a.y*b.y;
}
double dis (Point a, Point b) {//两个点的距离

    Point p = b-a; return p.len ();
}
double rad_degree (double rad) {//弧度转化为角度

    return rad/pi*180;
}
double rad (Point a, Point b) {//两个向量的夹角

    return fabs (atan2 (fabs (cross (a, b)), dot (a, b)) );
}
bool parallel (Point a, Point b) {//向量平行
    double p = rad (a, b);
    return dcmp (p) == 0 || dcmp (p-pi) == 0;
}

//************直线 线段
struct Line {
    Point s, e;//直线的两个点
    double k;//极角
    Line () {}
    Line (Point _s, Point _e) {
        s = _s, e = _e;
        k = atan2 (e.y - s.y,e.x - s.x);
    }
    //一个点和倾斜角确定直线
    Line (Point p, double ang) {
        k = ang;
        s = p;
        if (dcmp (ang-pi/2) == 0) {
            e = s + Point (0, 1);
        }
        else
            e = s + Point (1, tan (ang));
    }
    void input () {
        s.input ();
        e.input ();
    }
    void adjust () {
        if (e < s) swap (e, s);
    }
    double length () {//求线段长度
        return dis (s, e);
    }
    void get_angle () {
        k = atan2 (e.y - s.y,e.x - s.x);
    }
    double angle () {//直线的倾斜角
        if (dcmp (k) < 0) k += pi;
        if (dcmp (k-pi) == 0) k -= pi;
        return k;
    }
    Point operator &(const Line &b)const {//直线的交点(保证存在)
        Point res = s;
        double t = (cross (s - b.s, b.s - b.e))/cross (s - e, b.s - b.e); 
        res.x += (e.x - s.x)*t;
        res.y += (e.y - s.y)*t;
        return res;
    }
};

double polygon_area (Point *p, int n) {
    //n个点
    double area = 0;
    for (int i = 1; i < n-1; i++) {
        area += cross (p[i]-p[0], p[i+1]-p[0]);
    }
    return area/2;
}

bool HPIcmp (const Line &a, const Line &b) {
    if (fabs(a.k - b.k) > eps)
        return a.k < b.k;
    return cross (a.s - b.s, b.e - b.s) < 0; 
}
Line Q[maxn];
void HPI(Line line[], int n, Point res[], int &resn) {
    int tot = n; 
    sort(line,line+n,HPIcmp); 
    tot = 1;
    for(int i = 1;i < n;i++){
        if(fabs(line[i].k - line[i-1].k) > eps)
            line[tot++] = line[i];
    }
    int head = 0, tail = 1; 
    Q[0] = line[0];
    Q[1] = line[1];
    resn = 0;
    for (int i = 2; i < tot; i++) {
        if (fabs(cross (Q[tail].e-Q[tail].s, Q[tail-1].e-Q[tail-1].s)) < eps || fabs(cross (Q[head].e-Q[head].s, Q[head+1].e-Q[head+1].s)) < eps)
            return;
        while(head < tail && (cross ((Q[tail]&Q[tail-1])-line[i].s, line[i].e-line[i].s)) > eps) tail--;
        while(head < tail && (cross ((Q[head]&Q[head+1]) - line[i].s, line[i].e-line[i].s)) > eps)
        head++; 
        Q[++tail] = line[i];
    }
    while(head < tail && (cross ((Q[tail]&Q[tail-1]) - Q[head].s, Q[head].e-Q[head].s)) > eps)
        tail--;
    while(head < tail && (cross ((Q[head]&Q[head-1]) -Q[tail].s, Q[tail].e-Q[tail].e)) > eps) 
        head++;
    if(tail <= head + 1)
        return; 
    for(int i = head; i < tail; i++)
        res[resn++] = Q[i]&Q[i+1]; 
    if(head < tail - 1)
        res[resn++] = Q[head]&Q[tail];
}

int n, m;
Point p[maxn], ans[maxn];
Line hp[maxn];

bool ok (int x) {
    for (int i = 0; i < n; i++) {
        hp[i] = Line (p[i], p[(i+x+1)%n]);
    }
    HPI (hp, n, ans, m);
    if (fabs (polygon_area (ans, m)) >= eps)
        return 1;
    return 0;
}

int main () {
    int T;
    scanf ("%d", &T);
    while (T--) {
        scanf ("%d", &n);
        for (int i = n-1; i >= 0; i--) p[i].input ();
        if (n == 3) {
            printf ("1\n");
            continue;
        }
        int L = 1, R = n-2;
        while (R-L > 1) {
            int mid = (L+R)/2;
            if (ok (mid))
                L = mid;
            else 
                R = mid;
        }
        printf ("%d\n", ok (L) ? R : L);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值