Lightoj 1239 (平面几何 凸包)

题目链接:点击这里

题意:给出n个点, 要围成一个围栏, 围栏必须是凸的而且距离最近的点不能小于d. 求围栏周长的最小值.

显然是求出凸包以后凸包周长加上d为半径的圆周长就行了.

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

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 () {//顺时针旋转90return Point (-y, x);
    }
    Point rotate_right () {//逆时针旋转90return 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;//直线的两个点
    Line () {}
    Line (Point _s, Point _e) : s(_s), e(_e) {}
    //一个点和倾斜角确定直线
    Line (Point p, double ang) {
        s = p;
        if (dcmp (ang-pi/2) == 0) {
            e = s + Point (0, 1);
        }
        else
            e = s + Point (1, tan (ang));
    }
    //ax+by+c=0确定直线
    Line (double a, double b, double c) {
        if (dcmp (a) == 0) {
            s = Point (0, -c/b);
            e = Point (1, -c/b);
        }
        else if (dcmp (b) == 0) {
            s = Point (-c/a, 0);
            e = Point (-c/a, 1);
        }
        else {
            s = Point (0, -c/b);
            e = Point (1, (-c-a)/b);
        }
    }
    void input () {
        s.input ();
        e.input ();
    }
    void adjust () {
        if (e < s) swap (e, s);
    }
    double length () {//求线段长度
        return dis (s, e);
    }
    double angle () {//直线的倾斜角
        double k = atan2 (e.y-s.y, e.x-s.x);
        if (dcmp (k) < 0) k += pi;
        if (dcmp (k-pi) == 0) k -= pi;
        return k;
    }
};

int relation (Point p, Line l) {//点和直线的关系
    //1:在左侧 2:在右侧 3:在直线上
    int c = dcmp (cross (p-l.s, l.e-l.s));
    if (c < 0) return 1;
    else if (c > 0) return 2;
    else return 3;
}

bool point_on_seg (Point p, Line l) {//判断点在线段上
    return dcmp (cross (p-l.s, l.e-l.s)) == 0 &&
    dcmp (dot (p-l.s, p-l.e) <= 0);
    //如果忽略端点交点改成小于号就好了
}

bool parallel (Line a, Line b) {//直线平行
    return parallel (a.e-a.s, b.e-b.s);
}

int seg_cross_seg (Line a, Line v) {//线段相交判断
    //1:规范相交 2:不规范相交 3:不相交
    int d1 = dcmp (cross (a.e-a.s, v.s-a.s));
    int d2 = dcmp (cross (a.e-a.s, v.e-a.s));
    int d3 = dcmp (cross (v.e-v.s, a.s-v.s));
    int d4 = dcmp (cross (v.e-v.s, a.e-v.s));
    if ((d1^d2) == -2 && (d3^d4) == -2) return 2;
    return (d1 == 0 && dcmp (dot (v.s-a.s, v.s-a.e)) <= 0) ||
        (d2 == 0 && dcmp (dot (v.e-a.s, v.e-a.e)) <= 0) ||
        (d3 == 0 && dcmp (dot (a.s-v.s, a.s-v.e)) <= 0) ||
        (d4 == 0 && dcmp (dot (a.e-v.s, a.e-v.e)) <= 0);
}

int line_cross_seg (Line a, Line v) {//直线和线段相交判断
    //1:规范相交 2:非规范相交 3:不相交
    int d1 = dcmp (cross (a.e-a.s, v.s-a.s));
    int d2 = dcmp (cross (a.e-a.s, v.e-a.s));
    if (d1^d2 == -2) return 2;
    return (d1 == 0 || d2 == 0);
}

int line_cross_line (Line a, Line v) {//直线相交判断
    //0:平行 1:重合 2:相交
    if (parallel (a, v)) return relation (a.e, v) == 3;
    return 2;
}

Point line_intersection (Line a, Line v) {//直线交点
    //调用前确保有交点
    double a1 = cross (v.e-v.s, a.s-v.s);
    double a2 = cross (v.e-v.s, a.e-v.s);
    return Point ((a.s.x*a2-a.e.x*a1)/(a2-a1), (a.s.y*a2-a.e.y*a1)/(a2-a1));
}

double point_to_line (Point p, Line a) {//点到直线的距离
    return fabs (cross (p-a.s, a.e-a.s) / a.length ());
}

double point_to_seg (Point p, Line a) {//点到线段的距离
    if (dcmp (dot (p-a.s, a.e-a.s)) < 0 || dcmp (dot (p-a.e, a.s-a.e)) < 0)
        return min (dis (p, a.e), dis (p, a.s));
    return point_to_line (p, a);
}

Point projection (Point p, Line a) {//点在直线上的投影
    return a.s + (((a.e-a.s) * dot (a.e-a.s, p-a.s)) / (a.e-a.s).len2() );
}

Point symmetry (Point p, Line a) {//点关于直线的对称点
    Point q = projection (p, a);
    return Point (2*q.x-p.x, 2*q.y-p.y);
}

//***************圆
struct Circle {
    //圆心 半径
    Point p;
    double r;
    Circle () {}
    Circle (Point _p, double _r) : p(_p), r(_r) {}
    Circle (double a, double b, double _r) {
        p = Point (a, b);
        r = _r;
    }
    void input () {
        p.input ();
        scanf ("%lf", &r);
    }
    void output () {
        p.output ();
        printf (" %.2f\n", r);
    }
    bool operator == (const Circle &a) const {
        return p == a.p && (dcmp (r-a.r) == 0);
    }
    double area () {//面积
        return pi*r*r;
    }
    double circumference () {//周长
        return 2*pi*r;
    }
};

int relation (Point p, Circle a) {//点和圆的关系
    //0:圆外 1:圆上 2:圆内
    double d = dis (p, a.p);
    if (dcmp (d-a.r) == 0) return 1;
    return (dcmp (d-a.r) < 0 ? 2 : 0);
}

int relation (Line a, Circle b) {//直线和圆的关系
    //0:相离 1:相切 2:相交
    double p = point_to_line (b.p, a);
    if (dcmp (p-b.r) == 0) return 1;
    return (dcmp (p-b.r) < 0 ? 2 : 0);
}

int relation (Circle a, Circle v) {//两圆的位置关系
    //1:内含 2:内切 3:相交 4:外切 5:相离
    double d = dis (a.p, v.p);
    if (dcmp (d-a.r-v.r) > 0) return 5;
    if (dcmp (d-a.r-v.r) == 0) return 4;
    double l = fabs (a.r-v.r);
    if (dcmp (d-a.r-v.r) < 0 && dcmp (d-l) > 0) return 3;
    if (dcmp (d-l) == 0) return 2;
    if (dcmp (d-l) < 0) return 1;
}

Circle out_circle (Point a, Point b, Point c) {//三角形外接圆
    Line u = Line ((a+b)/2, ((a+b)/2) + (b-a).rotate_left ());
    Line v = Line ((b+c)/2, ((b+c)/2) + (c-b).rotate_left ());
    Point p = line_intersection (u, v);
    double r = dis (p, a);
    return Circle (p, r);
}

Circle in_circle (Point a, Point b, Point c) {//三角形内切圆
    Line u, v;
    double m = atan2 (b.y-a.y, b.x-a.x), n = atan2 (c.y-a.y, c.x-a.x);
    u.s = a;
    u.e = u.s+Point (cos ((n+m)/2), sin ((n+m)/2));
    v.s = b;
    m = atan2 (a.y-b.y, a.x-b.x), n = atan2 (c.y-b.y, c.x-b.x);
    v.e = v.s + Point (cos ((n+m)/2), sin ((n+m)/2));
    Point p = line_intersection (u, v);
    double r = point_to_seg (p, Line (a, b));
    return Circle (p, r);
}

int circle_intersection (Circle a, Circle v, Point &p1, Point &p2) {//两个圆的交点
    //返回交点个数 交点保存在引用中
    int rel = relation (a, v);
    if (rel == 1 || rel == 5) return 0;
    double d = dis (a.p, v.p);
    double l = (d*d + a.r*a.r - v.r*v.r) / (2*d);
    double h = sqrt (a.r*a.
                     r - l*l);
    Point tmp = a.p + (v.p-a.p).change_len (l);
    p1 = tmp + ((v.p-a.p).rotate_left ().change_len (h));
    p2 = tmp + ((v.p-a.p).rotate_right ().change_len (h));
    if (rel == 2 || rel == 4) return 1;
    return 2;
}

int line_cirlce_intersection (Line v, Circle u, Point &p1, Point &p2) {//直线和圆的交点
    //返回交点个数 交点保存在引用中
    if (!relation (v, u)) return 0;
    Point a = projection (u.p, v);
    double d = point_to_line (u.p, v);
    d = sqrt (u.r*u.r - d*d);
    if (dcmp (d) == 0) {
        p1 = a, p2 = a;
        return 1;
    }
    p1 = a + (v.e-v.s).change_len (d);
    p2 = a - (v.e-v.s).change_len (d);
    return 2;
}

int get_circle (Point a, Point b, double r1, Circle &c1, Circle &c2) {//得到过ab半径为r1的了两个圆
    //返回得到圆的个数 圆保存在两个引用中
    Circle x (a, r1), y (b, r1);
    int t = circle_intersection (x, y, c1.p, c2.p);
    if (!t) return 0;
    c1.r = c2.r = r1;
    return t;
}

int get_circle (Line u, Point q, double r1, Circle &c1, Circle &c2) {//得到和直线u相切 过点q 半径为r1的圆
    double d = point_to_line (q, u);
    if (dcmp (d-r1*2) > 0) return 0;
    if (dcmp (d) == 0) {
        c1.p = q + ((u.e-u.s).rotate_left ().change_len (r1));
        c2.p = q + ((u.e-u.s).rotate_right ().change_len (r1));
        c1.r = c2.r = r1;
        return 2;
    }
    Line u1 = Line (u.s + (u.e-u.s).rotate_left ().change_len (r1), u.e + (u.e-u.s).rotate_left ().change_len (r1));
    Line u2 = Line (u.s + (u.e-u.s).rotate_right ().change_len (r1), u.e + (u.e-u.s).rotate_right ().change_len (r1));
    Circle cc = Circle (q, r1);
    Point p1, p2;
    if (!line_cirlce_intersection (u1, cc, p1, p2))
        line_cirlce_intersection (u2, cc, p1, p2);
    c1 = Circle (p1, r1);
    if (p1 == p2) {
        c2 = c1;
        return 1;
    }
    c2 = Circle (p2, r1);
    return 2;
}

int get_circle (Line u, Line v, double r1, Circle &c1, Circle &c2, Circle &c3, Circle &c4) {//和直线u,v相切 半径为r1的圆
    if (parallel (u, v)) return 0;
    Line u1 = Line (u.s + (u.e-u.s).rotate_left ().change_len (r1), u.e + (u.e-u.s).rotate_left ().change_len (r1));
    Line u2 = Line (u.s + (u.e-u.s).rotate_right ().change_len (r1), u.e + (u.e-u.s).rotate_right ().change_len (r1));
    Line v1 = Line (v.s + (v.e-v.s).rotate_left ().change_len (r1), v.e + (v.e-v.s).rotate_left ().change_len (r1));
    Line v2 = Line (v.s + (v.e-v.s).rotate_right ().change_len (r1), v.e + (v.e-v.s).rotate_right ().change_len (r1));
    c1.r = c2.r = c3.r = c4.r = r1;
    c1.p = line_intersection (u1, v1);
    c2.p = line_intersection (u1, v2);
    c3.p = line_intersection (u2, v1);
    c4.p = line_intersection (u2, v2);
    return 4;
}

int get_circle (Circle cx, Circle cy, double r1, Circle &c1, Circle &c2) {//和两个圆相切 半径为r1的圆
    //确保两个圆外离
    Circle x (cx.p, r1+cx.r), y (cy.p, r1+cy.r);
    int t = circle_intersection (x, y, c1.p, c2.p);
    if (!t) return 0;
    c1.r = c2.r = r1;
    return t;
}

int tan_line (Point q, Circle a, Line &u, Line &v) {//过一点作圆切线
    int x = relation (q, a);
    if (x == 2) return 0;
    if (x == 1) {
        u = Line (q, q + (q-a.p).rotate_left ());
        v = u;
        return 1;
    }
    double d = dis (a.p, q);
    double l = a.r*a.r/d;
    double h = sqrt (a.r*a.r - l*l);
    u = Line (q, a.p + (q-a.p).change_len (l) + (q-a.p).rotate_left ().change_len (h));
    v = Line (q, a.p + (q-a.p).change_len (l) + (q-a.p).rotate_right ().change_len (h));
    return 2;
}

double area_circle (Circle a, Circle v) {//两圆相交面积
    int rel = relation (a, v);
    if (rel >= 4) return 0;
    if (rel <= 2) return min (a.area (), v.area ());
    double d = dis (a.p, v.p);
    double hf = (a.r+v.r+d)/2;
    double ss = 2*sqrt (hf*(hf-a.r)*(hf-v.r)*(hf-d));
    double a1 = acos ((a.r*a.r+d*d-v.r*v.r) / (2*a.r*d));
    a1 = a1*a.r*a.r;
    double a2 = acos ((v.r*v.r+d*d-a.r*a.r) / (2*v.r*d));
    a2 = a2*v.r*v.r;
    return a1+a2-ss;
}

double circle_traingle_area (Point a, Point b, Circle c) {//圆心三角形的面积
    Point p = c.p; double r = c.r;
    if (dcmp (cross (p-a, p-b)) == 0) return 0;
    Point q[5];
    int len = 0;
    q[len++] = a;
    Line l(a, b);
    Point p1, p2;
    if (line_cirlce_intersection (l, c, q[1], q[2]) == 2) {
        if (dcmp (dot (a-q[1], b-q[1])) < 0) q[len++] = q[1];
        if (dcmp (dot (a-q[2], b-q[2])) < 0) q[len++] = q[2];
    }
    q[len++] = b;
    if (len == 4 && dcmp (dot (q[0]-q[1], q[2]-q[1])) > 0)
        swap (q[1], q[2]);
    double res = 0;
    for (int i = 0; i < len-1; i++) {
        if (relation (q[i], c) == 0 || relation (q[i+1], c) == 0) {
            double arg = rad (q[i]-p, q[i+1]-p);
            res += r*r*arg/2.0;
        }
        else {
            res += fabs (cross (q[i]-p, q[i+1]-p))/2;
        }
    }
    return res;
}

//*************多边形
bool is_convex (Point *p, int n) {//判断n边行是不是凸的
    bool s[2];
    s[0] = s[1] = 0;
    for (int i = 0; i < n; i++) {
        int j = (i+1)%n;
        int k = (j+1)%n;
        s[dcmp (cross (p[j]-p[i], p[k]-p[i]))+1] = 1;
        if (s[0] && s[2]) return 0;
    }
    return 1;
}

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 relation (Point a, Point *b, int n) {//点和多边形的关系(凸凹都可以)
    //0:外部 1:内部 2:边上 3:顶点
    int w = 0;
    for (int i = 0; i < n; i++) {
        if (a == b[i] || a == b[(i+1)%n])
            return 3;
        if (point_on_seg (a, Line (b[(i+1)%n], b[i])))
            return 2;
        int k = dcmp (cross (b[(i+1)%n]-b[i], a-b[i]));
        int d1 = dcmp (b[i].y - a.y);
        int d2 = dcmp (b[(i+1)%n].y - a.y);
        if (k > 0 && d1 <= 0 && d2 > 0)
            w++;
        if (k < 0 && d2 <= 0 && d1 > 0)
            w--;
    }
    if (w != 0)
        return 1;
    return 0;
}

int convex_cut (Line u, Point *p, int n, Point *po) {//直线切割多边形左侧
    //返回切割后多边形的数量
    int top = 0;
    for (int i = 0; i < n; i++) {
        int d1 = dcmp (cross (u.e-u.s, p[i]-u.s));
        int d2 = dcmp (cross (u.e-u.s, p[(i+1)%n]-u.s));
        if (d1 >= 0) po[top++] = p[i];
        if (d1*d2 < 0) po[top++] = line_intersection (u, Line (p[i], p[(i+1)%n]));
    }
    return top;
}

double convex_circumference (Point *p, int n) {//多边形的周长(凹凸都可以)
    double ans = 0;
    for (int i = 0; i < n; i++) {
        ans += dis (p[i], p[(i+1)%n]);
    }
    return ans;
}

int convex_hull (Point *p, Point *ch, int n) {//求凸包
    //所有的点集 凸包点集 点集的点数
    sort (p, p+n);
    int m = 0;
    for (int i = 0; i < n; i++) {
        while (m > 1 && cross (ch[m-1]-ch[m-2], p[i]-ch[m-1]) <= 0)
            m--;
        ch[m++] = p[i];
    }
    int k = m;
    for (int i = n-2; i >= 0; i--) {
        while (m > k && cross (ch[m-1]-ch[m-2], p[i]-ch[m-1]) <= 0)
            m--;
        ch[m++] = p[i];
    }
    if (n > 1)
        m--;
    return m;
}

#define maxn 51111
int n, m;
double d;
double ans;
Point p[maxn], ch[maxn];

int main () {
    int t, kase = 0;
    cin >> t;
    while (t--) {
        cin >> n >> d;
        for (int i = 0; i < n; i++) p[i].input ();
        m = convex_hull (p, ch, n);
        ans = convex_circumference (ch, m) + 2*pi*d;
        printf ("Case %d: %.7f\n", ++kase, ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值