zoukankan      html  css  js  c++  java
  • [kuangbin]计算几何之凸包问题

    POJ 1113 Wall

    大意:

    给出一个n边形的城堡,国王要求修建一个城墙,城墙到城堡的距离最少是L,问最少的城堡边长是多少,答案要求四舍五入到整数

    img

    思路:

    很容易想到求凸包,但是要求到城堡的距离最少是L,所以仅仅求凸包是不够的,观察图像可以发现,最优的方法是凸包的每条边向外移动L,然后用圆形填充缝隙即可,也就是说,最终答案 是凸包边长+半径为L的圆的周长。

    剩下就是纯模板了,注意答案要四舍五入,直接

    printf("%d
    ",(int)(res+0.5));
    

    即可.

    #include <iostream>
    #include <string.h>
    #include <stdio.h>
    #include <vector>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <math.h>
    #include <cstdio>
    #include <set>
    
    using namespace std;
    
    const int N = 20 + 5;
    typedef long long LL;
    int t, n;
    double dis[100][100];
    // 计算几何模板
    const double eps = 1e-8;
    const double inf = 1e20;
    const double pi = acos(-1.0);
    const int maxp = 1010;
    // 和0做比较
    int sgn(double x) {
        if (fabs(x) < eps) return 0;  // =0
        if (x < 0)
            return -1;  // < 0
        else
            return 1;  // > 0
    }
    // 计算x的平方
    inline double sqr(double x) { return x * x; }
    
    struct Point {
        double x, y;
        int index;
        Point() {}
        Point(double _x, double _y) {
            x = _x;
            y = _y;
        }
        void input() { scanf("%lf%lf", &x, &y); }
        void output() { printf("%.2f %.2f
    ", x, y); }
        bool operator==(Point b) const {
            return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
        }
        bool operator<(Point b) const {
            return sgn(x - b.x) == 0 ? sgn(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); }
        //叉积
        double operator^(const Point &b) const { return x * b.y - y * b.x; }
        //点积
        double operator*(const Point &b) const { return x * b.x + y * b.y; }
        //返回两点的距离
        double dist(Point p) { return hypot(x - p.x, y - p.y); }
         // 极角排序
        
    
    };
    
    struct Line {
        Point s, e;
        Line() {}
        // 两点确定一条线段
        Line(Point _s, Point _e) {
            s = _s;
            e = _e;
        }
        //返回点和直线(方向向量)关系
        // 1  在左侧; 2  在右侧; 3  在直线上
        int relation(Point p) {
            int c = sgn((p - s) ^ (e - s));
            if (c < 0)
                return 1;
            else if (c > 0)
                return 2;
            else
                return 3;
        }
        //两线段相交判断:规范相交:交点不在端点
        // 2 规范相交;1 非规范相交;0 不相交
         int segcrossseg(Line v) {
            int d1 = sgn((e - s) ^ (v.s - s));
            int d2 = sgn((e - s) ^ (v.e - s));
            int d3 = sgn((v.e - v.s) ^ (s - v.s));
            int d4 = sgn((v.e - v.s) ^ (e - v.s));
            if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;
            return (d1 == 0 && sgn((v.s - s) * (v.s - e)) <= 0) ||
                   (d2 == 0 && sgn((v.e - s) * (v.e - e)) <= 0) ||
                   (d3 == 0 && sgn((s - v.s) * (s - v.e)) <= 0) ||
                   (d4 == 0 && sgn((e - v.s) * (e - v.e)) <= 0);
        }
        bool parallel(Line v) { return sgn((e - s) ^ (v.e - v.s)) == 0; }
        //求两直线的交点
        //要保证两直线不平行或重合
        Point crosspoint(Line v) {
            double a1 = (v.e - v.s) ^ (s - v.s);
            double a2 = (v.e - v.s) ^ (e - v.s);
            return Point((s.x * a2 - e.x * a1) / (a2 - a1),
                         (s.y * a2 - e.y * a1) / (a2 - a1));
        }
        double length() { return s.dist(e); }
        //点到直线的距离
        double dispointtoline(Point p) {
            return fabs((p - s) ^ (e - s)) / length();
        }
        //点到线段的距离
        double dispointtoseg(Point p) {
            if (sgn((p - s) * (e - s)) < 0 || sgn((p - e) * (s - e)) < 0)
                return min(p.dist(s), p.dist(e));
            return dispointtoline(p);
        }
        //直线和线段相交判断
        //-*this line   -v seg
        // 2 规范相交
        // 1 非规范相交
        // 0 不相交
        int linecrossseg(Line v) {
            int d1 = sgn((e - s) ^ (v.s - s));
            int d2 = sgn((e - s) ^ (v.e - s));
            if ((d1 ^ d2) == -2) return 2;
            return (d1 == 0 || d2 == 0);
        }
    };
    
    struct polygon {
        int n;
        Point p[maxp];
        Line l[maxp];
        // 输入多边形
        void input(int _n) {
            n = _n;
            for (int i = 0; i < n; i++) p[i].input();
        }
        // 加入一个点
        void add(Point q) { p[n++] = q; }
        // 得到多边形所有边的线段
        void getline() {
            for (int i = 0; i < n; i++) {
                l[i] = Line(p[i], p[(i + 1) % n]);
            }
        }
        // 极角排序
        struct cmp {
            Point p;
            cmp(const Point &p0) { p = p0; }
            bool operator()(const Point &aa, const Point &bb) {
                Point a = aa, b = bb;
                int d = sgn((a - p) ^ (b - p));
                if (d == 0) {
                    return sgn(a.dist(p) - b.dist(p)) < 0;
                }
                return d > 0;
            }
        };
        // 进行极角排序
        // 首先需要找到最左下角的点
        // 需要重载号好Point的 < 操作符(min函数要用)
        void norm() {
            Point mi = p[0];
            for (int i = 1; i < n; i++)
                mi = min(mi, p[i]);  // min函数用Point的<重载
            sort(p, p + n, cmp(mi));
        }
        // 得到凸包
        // 得到的凸包里面的点编号是0 ~ n-1的
        // 两种凸包的方法
        // 注意如果有影响,要特判下所有点共点,或者共线的特殊情况
        void getconvex(polygon &convex) {
            sort(p, p + n);
            convex.n = n;
            for (int i = 0; i < min(n, 2); i++) {
                convex.p[i] = p[i];
            }
            if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n--;  //特判
            if (n <= 2) return;
            int &top = convex.n;
            top = 1;
            for (int i = 2; i < n; i++) {
                while (top && sgn((convex.p[top] - p[i]) ^
                                  (convex.p[top - 1] - p[i])) <= 0)
                    top--;
                convex.p[++top] = p[i];
            }
            int temp = top;
            convex.p[++top] = p[n - 2];
            for (int i = n - 3; i >= 0; i--) {
                while (top != temp && sgn((convex.p[top] - p[i]) ^
                                          (convex.p[top - 1] - p[i])) <= 0)
                    top--;
                convex.p[++top] = p[i];
            }
            if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n--;  //特判
            convex.norm();  //`原来得到的是顺时针的点,排序后逆时针`
        }
        // 得到凸包的另外一种方法
        void Graham(polygon &convex) {
            norm();
            int &top = convex.n;
            top = 0;
            if (n == 1) {
                top = 1;
                convex.p[0] = p[0];
                return;
            }
            if (n == 2) {
                top = 2;
                convex.p[0] = p[0];
                convex.p[1] = p[1];
                if (convex.p[0] == convex.p[1]) top--;
                return;
            }
            convex.p[0] = p[0];
            convex.p[1] = p[1];
            top = 2;
            for (int i = 2; i < n; i++) {
                while (top > 1 && sgn((convex.p[top - 1] - convex.p[top - 2]) ^
                                      (p[i] - convex.p[top - 2])) <= 0)
                    top--;
                convex.p[top++] = p[i];
            }
            if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n--;  //特判
        }
        
        
        // 得到多边形的周长
        double getcircumference() {
            double sum = 0;
            for (int i = 0; i < n; i++) {
                sum += p[i].dist(p[(i + 1) % n]);
            }
            return sum;
        }
        
    };
    double d;
    polygon P;
    int main(){
        cin >> n >> d;
        for (int i = 0; i < n;i++){
            double x, y;
            scanf("%lf%lf", &x, &y);
            P.add(Point(x, y));
        }
        polygon wal;
        P.Graham(wal);
        printf("%d
    ",(int)(wal.getcircumference()+2*pi*d+0.5));
    }
    

    POJ 2007 Scrambled Polygon

    大意:

    讲了一堆概念...极角排序即可

    思路:

    模板题

    #include <iostream>
    #include <string.h>
    #include <stdio.h>
    #include <vector>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <math.h>
    #include <cstdio>
    #include <set>
    
    using namespace std;
    
    const int N = 20 + 5;
    typedef long long LL;
    int t, n;
    double dis[100][100];
    // 计算几何模板
    const double eps = 1e-8;
    const double inf = 1e20;
    const double pi = acos(-1.0);
    const int maxp = 1010;
    // 和0做比较
    int sgn(double x) {
        if (fabs(x) < eps) return 0;  // =0
        if (x < 0)
            return -1;  // < 0
        else
            return 1;  // > 0
    }
    // 计算x的平方
    inline double sqr(double x) { return x * x; }
    
    struct Point {
        double x, y;
        int index;
        Point() {}
        Point(double _x, double _y) {
            x = _x;
            y = _y;
        }
        void input() { scanf("%lf%lf", &x, &y); }
        void output() { printf("%.2f %.2f
    ", x, y); }
        bool operator==(Point b) const {
            return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
        }
        bool operator<(Point b) const {
            return sgn(x - b.x) == 0 ? sgn(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); }
        //叉积
        double operator^(const Point &b) const { return x * b.y - y * b.x; }
        //点积
        double operator*(const Point &b) const { return x * b.x + y * b.y; }
        //返回两点的距离
        double dist(Point p) { return hypot(x - p.x, y - p.y); }
         // 极角排序
        
    
    };
    
    struct Line {
        Point s, e;
        Line() {}
        // 两点确定一条线段
        Line(Point _s, Point _e) {
            s = _s;
            e = _e;
        }
        //返回点和直线(方向向量)关系
        // 1  在左侧; 2  在右侧; 3  在直线上
        int relation(Point p) {
            int c = sgn((p - s) ^ (e - s));
            if (c < 0)
                return 1;
            else if (c > 0)
                return 2;
            else
                return 3;
        }
        //两线段相交判断:规范相交:交点不在端点
        // 2 规范相交;1 非规范相交;0 不相交
         int segcrossseg(Line v) {
            int d1 = sgn((e - s) ^ (v.s - s));
            int d2 = sgn((e - s) ^ (v.e - s));
            int d3 = sgn((v.e - v.s) ^ (s - v.s));
            int d4 = sgn((v.e - v.s) ^ (e - v.s));
            if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;
            return (d1 == 0 && sgn((v.s - s) * (v.s - e)) <= 0) ||
                   (d2 == 0 && sgn((v.e - s) * (v.e - e)) <= 0) ||
                   (d3 == 0 && sgn((s - v.s) * (s - v.e)) <= 0) ||
                   (d4 == 0 && sgn((e - v.s) * (e - v.e)) <= 0);
        }
        bool parallel(Line v) { return sgn((e - s) ^ (v.e - v.s)) == 0; }
        //求两直线的交点
        //要保证两直线不平行或重合
        Point crosspoint(Line v) {
            double a1 = (v.e - v.s) ^ (s - v.s);
            double a2 = (v.e - v.s) ^ (e - v.s);
            return Point((s.x * a2 - e.x * a1) / (a2 - a1),
                         (s.y * a2 - e.y * a1) / (a2 - a1));
        }
        double length() { return s.dist(e); }
        //点到直线的距离
        double dispointtoline(Point p) {
            return fabs((p - s) ^ (e - s)) / length();
        }
        //点到线段的距离
        double dispointtoseg(Point p) {
            if (sgn((p - s) * (e - s)) < 0 || sgn((p - e) * (s - e)) < 0)
                return min(p.dist(s), p.dist(e));
            return dispointtoline(p);
        }
        //直线和线段相交判断
        //-*this line   -v seg
        // 2 规范相交
        // 1 非规范相交
        // 0 不相交
        int linecrossseg(Line v) {
            int d1 = sgn((e - s) ^ (v.s - s));
            int d2 = sgn((e - s) ^ (v.e - s));
            if ((d1 ^ d2) == -2) return 2;
            return (d1 == 0 || d2 == 0);
        }
    };
    
    struct polygon {
        int n;
        Point p[maxp];
        Line l[maxp];
        // 输入多边形
        void input(int _n) {
            n = _n;
            for (int i = 0; i < n; i++) p[i].input();
        }
        // 加入一个点
        void add(Point q) { p[n++] = q; }
        // 得到多边形所有边的线段
        void getline() {
            for (int i = 0; i < n; i++) {
                l[i] = Line(p[i], p[(i + 1) % n]);
            }
        }
        // 极角排序
        struct cmp {
            Point p;
            cmp(const Point &p0) { p = p0; }
            bool operator()(const Point &aa, const Point &bb) {
                Point a = aa, b = bb;
                int d = sgn((a - p) ^ (b - p));
                if (d == 0) {
                    return sgn(a.dist(p) - b.dist(p)) < 0;
                }
                return d > 0;
            }
        };
        // 进行极角排序
        // 首先需要找到最左下角的点
        // 需要重载号好Point的 < 操作符(min函数要用)
        void norm() {
            Point mi = Point(0, 0);
            sort(p, p + n, cmp(mi));
        }
        
    };
    double d;
    polygon P;
    int main(){
        double x, y;
        while(scanf("%lf%lf", &x, &y)!=EOF){
            P.add(Point(x, y));
        }
        P.norm();
        for (int i = 0; i < P.n;i++){
            printf("(%.0f,%.0f)
    ", P.p[i].x, P.p[i].y);
        }
    }
    

    POJ 1873 The Fortified Forest

    大意:

    给出n((n<=15))颗树,每颗树都有自己的价值和木材值,现在需要砍掉一些树用他们的木材值做围栏围住其他的树,问能保留最大价值的方案是什么,如果价值相同,则选择砍树颗数最少的

    思路:

    因为n很小,所以可以想到二进制枚举,然后计算没被砍的树的凸包即可

    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    
    #include <algorithm>
    #include <cstdio>
    #include <iostream>
    #include <map>
    #include <queue>
    #include <set>
    #include <vector>
    
    using namespace std;
    
    const int N = 20 + 5;
    typedef long long LL;
    double dis[100][100];
    // 计算几何模板
    const double eps = 1e-8;
    const double inf = 1e20;
    const double pi = acos(-1.0);
    const int maxp = 20;
    // 和0做比较
    int sgn(double x) {
      if (fabs(x) < eps) return 0;  // =0
      if (x < 0)
        return -1;  // < 0
      else
        return 1;  // > 0
    }
    // 计算x的平方
    inline double sqr(double x) { return x * x; }
    
    struct Point {
      double x, y;
      int index;
      Point() {}
      Point(double _x, double _y) {
        x = _x;
        y = _y;
      }
      void input() { scanf("%lf%lf", &x, &y); }
      void output() { printf("%.2f %.2f
    ", x, y); }
      bool operator==(Point b) const {
        return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
      }
      bool operator<(Point b) const {
        return sgn(x - b.x) == 0 ? sgn(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); }
      //叉积
      double operator^(const Point &b) const { return x * b.y - y * b.x; }
      //点积
      double operator*(const Point &b) const { return x * b.x + y * b.y; }
      //返回两点的距离
      double dist(Point p) {
        return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
      }
    
      // 极角排序
    };
    struct polygon {
      int n;
      Point p[maxp];
      // 输入多边形
      void input(int _n) {
        n = _n;
        for (int i = 0; i < n; i++) p[i].input();
      }
      // 加入一个点
      void add(Point q) { p[n++] = q; }
      // 得到多边形所有边的线段
    
      // 极角排序
      struct cmp {
        Point p;
        cmp(const Point &p0) { p = p0; }
        bool operator()(const Point &aa, const Point &bb) {
          Point a = aa, b = bb;
          int d = sgn((a - p) ^ (b - p));
          if (d == 0) {
            return sgn(a.dist(p) - b.dist(p)) < 0;
          }
          return d > 0;
        }
      };
      // 进行极角排序
      // 首先需要找到最左下角的点
      // 需要重载号好Point的 < 操作符(min函数要用)
      void norm() {
        Point mi = p[0];
        for (int i = 1; i < n; i++) mi = min(mi, p[i]);  // min函数用Point的<重载
        sort(p, p + n, cmp(mi));
      }
      void Graham(polygon &convex) {
        norm();
        int &top = convex.n;
        top = 0;
        if (n == 1) {
          top = 1;
          convex.p[0] = p[0];
          return;
        }
        if (n == 2) {
          top = 2;
          convex.p[0] = p[0];
          convex.p[1] = p[1];
          if (convex.p[0] == convex.p[1]) top--;
          return;
        }
        convex.p[0] = p[0];
        convex.p[1] = p[1];
        top = 2;
        for (int i = 2; i < n; i++) {
          while (top > 1 && sgn((convex.p[top - 1] - convex.p[top - 2]) ^
                                (p[i] - convex.p[top - 2])) <= 0)
            top--;
          convex.p[top++] = p[i];
        }
        if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n--;  //特判
      }
    
      // 得到多边形的周长
      double getcircumference() {
        double sum = 0;
        for (int i = 0; i < n; i++) {
          sum += p[i].dist(p[(i + 1) % n]);
        }
        return sum;
      }
    };
    
    double v[20], l[20];
    polygon P,temp,t;
    int main() {
      double x, y;
      int k = 0, n;
      while (scanf("%d", &n) != EOF && n != 0) {
        k++;
        
        P.n = 0;
        for (int i = 0; i < n; i++) {
          scanf("%lf%lf%lf%lf", &x, &y, &v[i], &l[i]);
          P.add(Point(x, y));
        }
        int MN = 1 << n;
        double maxvul = 0.0, extra = 0;
        int res[20], cnt = 0;
        for (int i = 0; i < MN; i++) {
          
          temp.n = 0;
          double wood = 0, vul = 0;
          int tempcnt = 0;
          int havecnt = 0;
          for (int j = 0; j < n; j++) {
            if (i & (1 << j)) {
              temp.add(P.p[j]);
              vul += v[j];
            } else {
              wood += l[j];
              tempcnt++;
            }
          }
          t.n = 0;
          temp.Graham(t);
          double need = t.getcircumference();
    
          if (wood >= need) {
            if (maxvul < vul || (maxvul == vul && cnt > tempcnt)) {
              maxvul = vul;
              cnt = 0;
              for (int j = 0; j < n; j++) {
                if (!(i & (1 << j))) {
                  res[cnt++] = j;
                }
              }
              extra = wood - need;
            }
          }
        }
    
        if (k != 1) {
          printf("
    ");
        }
        printf("Forest %d
    ", k);
        printf("Cut these trees:");
        for (int i = 0; i < cnt; i++) {
          printf(" %d", res[i] + 1);
        }
        printf("
    Extra wood: %.2lf
    ", extra);
      }
    }
    

    POJ 1228 Grandpa's Estate

    大意:

    原本有一个凸包,现在其中的一部分点已经丢失了,现在给你剩余的点,问根据这些点能否确定原本的凸包形状

    思路:

    判断是否是稳定凸包即可,即凸包上每个边都有至少三个点。记得特判所有的点共线的情况。

    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    
    #include <algorithm>
    #include <cstdio>
    #include <iostream>
    #include <map>
    #include <queue>
    #include <set>
    #include <vector>
    
    using namespace std;
    
    const int N = 20 + 5;
    typedef long long LL;
    double dis[100][100];
    // 计算几何模板
    const double eps = 1e-8;
    const double inf = 1e20;
    const double pi = acos(-1.0);
    const int maxp = 1010;
    // 和0做比较
    int sgn(double x) {
        if (fabs(x) < eps) return 0;  // =0
        if (x < 0)
            return -1;  // < 0
        else
            return 1;  // > 0
    }
    // 计算x的平方
    inline double sqr(double x) { return x * x; }
    
    struct Point {
        double x, y;
        int index;
        Point() {}
        Point(double _x, double _y) {
            x = _x;
            y = _y;
        }
        void input() { scanf("%lf%lf", &x, &y); }
        void output() { printf("%.2f %.2f
    ", x, y); }
        bool operator==(Point b) const {
            return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
        }
        bool operator<(Point b) const {
            return sgn(x - b.x) == 0 ? sgn(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); }
        //叉积
        double operator^(const Point &b) const { return x * b.y - y * b.x; }
        //点积
        double operator*(const Point &b) const { return x * b.x + y * b.y; }
        //返回两点的距离
        double dist(Point p) {
            return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
        }
    
        // 极角排序
    };
    
    struct Line {
        Point s, e;
        Line() {}
        // 两点确定一条线段
        Line(Point _s, Point _e) {
            s = _s;
            e = _e;
        }
        //返回点和直线(方向向量)关系
        // 1  在左侧; 2  在右侧; 3  在直线上
        int relation(Point p) {
            int c = sgn((p - s) ^ (e - s));
            if (c < 0)
                return 1;
            else if (c > 0)
                return 2;
            else
                return 3;
        }
        //两线段相交判断:规范相交:交点不在端点
        // 2 规范相交;1 非规范相交;0 不相交
        int segcrossseg(Line v) {
            int d1 = sgn((e - s) ^ (v.s - s));
            int d2 = sgn((e - s) ^ (v.e - s));
            int d3 = sgn((v.e - v.s) ^ (s - v.s));
            int d4 = sgn((v.e - v.s) ^ (e - v.s));
            if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;
            return (d1 == 0 && sgn((v.s - s) * (v.s - e)) <= 0) ||
                   (d2 == 0 && sgn((v.e - s) * (v.e - e)) <= 0) ||
                   (d3 == 0 && sgn((s - v.s) * (s - v.e)) <= 0) ||
                   (d4 == 0 && sgn((e - v.s) * (e - v.e)) <= 0);
        }
        bool parallel(Line v) { return sgn((e - s) ^ (v.e - v.s)) == 0; }
        //求两直线的交点
        //要保证两直线不平行或重合
        Point crosspoint(Line v) {
            double a1 = (v.e - v.s) ^ (s - v.s);
            double a2 = (v.e - v.s) ^ (e - v.s);
            return Point((s.x * a2 - e.x * a1) / (a2 - a1),
                         (s.y * a2 - e.y * a1) / (a2 - a1));
        }
        double length() { return s.dist(e); }
        //点到直线的距离
        double dispointtoline(Point p) {
            return fabs((p - s) ^ (e - s)) / length();
        }
        //点到线段的距离
        double dispointtoseg(Point p) {
            if (sgn((p - s) * (e - s)) < 0 || sgn((p - e) * (s - e)) < 0)
                return min(p.dist(s), p.dist(e));
            return dispointtoline(p);
        }
        //直线和线段相交判断
        //-*this line   -v seg
        // 2 规范相交
        // 1 非规范相交
        // 0 不相交
        int linecrossseg(Line v) {
            int d1 = sgn((e - s) ^ (v.s - s));
            int d2 = sgn((e - s) ^ (v.e - s));
            if ((d1 ^ d2) == -2) return 2;
            return (d1 == 0 || d2 == 0);
        }
    };
    
    struct polygon {
        int n;
        Point p[maxp];
        // 输入多边形
        void input(int _n) {
            n = _n;
            for (int i = 0; i < n; i++) p[i].input();
        }
        // 加入一个点
        void add(Point q) { p[n++] = q; }
        // 得到多边形所有边的线段
    
        // 极角排序
        struct cmp {
            Point p;
            cmp(const Point &p0) { p = p0; }
            bool operator()(const Point &aa, const Point &bb) {
                Point a = aa, b = bb;
                int d = sgn((a - p) ^ (b - p));
                if (d == 0) {
                    return sgn(a.dist(p) - b.dist(p)) < 0;
                }
                return d > 0;
            }
        };
        // 进行极角排序
        // 首先需要找到最左下角的点
        // 需要重载号好Point的 < 操作符(min函数要用)
        void norm() {
            Point mi = p[0];
            for (int i = 1; i < n; i++)
                mi = min(mi, p[i]);  // min函数用Point的<重载
            sort(p, p + n, cmp(mi));
        }
        void Graham(polygon &convex) {
            norm();
            int &top = convex.n;
            top = 0;
            if (n == 1) {
                top = 1;
                convex.p[0] = p[0];
                return;
            }
            if (n == 2) {
                top = 2;
                convex.p[0] = p[0];
                convex.p[1] = p[1];
                if (convex.p[0] == convex.p[1]) top--;
                return;
            }
            convex.p[0] = p[0];
            convex.p[1] = p[1];
            top = 2;
            for (int i = 2; i < n; i++) {
                while (top > 1 && sgn((convex.p[top - 1] - convex.p[top - 2]) ^
                                      (p[i] - convex.p[top - 2])) <= 0)
                    top--;
                convex.p[top++] = p[i];
            }
            if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n--;  //特判
        }
    
        // 得到多边形的周长
        double getcircumference() {
            double sum = 0;
            for (int i = 0; i < n; i++) {
                sum += p[i].dist(p[(i + 1) % n]);
            }
            return sum;
        }
    };
    int t, n;
    double v[20], l[20];
    polygon P, tb;
    int main() {
        scanf("%d", &t);
        while (t--) {
            P.n = 0;
            scanf("%d", &n);
            for (int i = 0; i < n; i++) {
                double x, y;
                scanf("%lf%lf", &x, &y);
                P.add(Point(x, y));
            }
            tb.n = 0;
            P.Graham(tb);
            if (tb.n <= 2) {
                cout << "NO" << endl;
                continue;
            }
            int flag = 1;
            for (int i = 0; i < tb.n; i++) {
                Line now = Line(tb.p[i], tb.p[(i + 1) % tb.n]);
                int num = 0;
                for (int j = 0; j < P.n; j++) {
                    if (now.relation(P.p[j]) == 3) {
                        num++;
                    }
                }
                if (num < 3) {
                    flag = 0;
                }
            }
            if (flag)
                cout << "YES" << endl;
            else
                cout << "NO" << endl;
        }
    }
    

    POJ 3348 Cows

    大意:

    每个奶牛至少需要50平方米的土地,给出n个树,问从这些树里面选出牧场的端点,最多能养多少奶牛

    思路:

    凸包求面积,除以50即可,模板题,求面积利用原点到顶点组成的三角形叉积和即可。

    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    
    #include <algorithm>
    #include <cstdio>
    #include <iostream>
    #include <map>
    #include <queue>
    #include <set>
    #include <vector>
    
    using namespace std;
    
    const int N = 20 + 5;
    typedef long long LL;
    double dis[100][100];
    // 计算几何模板
    const double eps = 1e-8;
    const double inf = 1e20;
    const double pi = acos(-1.0);
    const int maxp = 10000 + 5;
    // 和0做比较
    int sgn(double x) {
        if (fabs(x) < eps) return 0;  // =0
        if (x < 0)
            return -1;  // < 0
        else
            return 1;  // > 0
    }
    // 计算x的平方
    inline double sqr(double x) { return x * x; }
    
    struct Point {
        double x, y;
        int index;
        Point() {}
        Point(double _x, double _y) {
            x = _x;
            y = _y;
        }
        void input() { scanf("%lf%lf", &x, &y); }
        void output() { printf("%.2f %.2f
    ", x, y); }
        bool operator==(Point b) const {
            return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
        }
        bool operator<(Point b) const {
            return sgn(x - b.x) == 0 ? sgn(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); }
        //叉积
        double operator^(const Point &b) const { return x * b.y - y * b.x; }
        //点积
        double operator*(const Point &b) const { return x * b.x + y * b.y; }
        //返回两点的距离
        double dist(Point p) {
            return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
        }
    
        // 极角排序
    };
    
    struct Line {
        Point s, e;
        Line() {}
        // 两点确定一条线段
        Line(Point _s, Point _e) {
            s = _s;
            e = _e;
        }
        //返回点和直线(方向向量)关系
        // 1  在左侧; 2  在右侧; 3  在直线上
        int relation(Point p) {
            int c = sgn((p - s) ^ (e - s));
            if (c < 0)
                return 1;
            else if (c > 0)
                return 2;
            else
                return 3;
        }
        //两线段相交判断:规范相交:交点不在端点
        // 2 规范相交;1 非规范相交;0 不相交
        int segcrossseg(Line v) {
            int d1 = sgn((e - s) ^ (v.s - s));
            int d2 = sgn((e - s) ^ (v.e - s));
            int d3 = sgn((v.e - v.s) ^ (s - v.s));
            int d4 = sgn((v.e - v.s) ^ (e - v.s));
            if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;
            return (d1 == 0 && sgn((v.s - s) * (v.s - e)) <= 0) ||
                   (d2 == 0 && sgn((v.e - s) * (v.e - e)) <= 0) ||
                   (d3 == 0 && sgn((s - v.s) * (s - v.e)) <= 0) ||
                   (d4 == 0 && sgn((e - v.s) * (e - v.e)) <= 0);
        }
        bool parallel(Line v) { return sgn((e - s) ^ (v.e - v.s)) == 0; }
        //求两直线的交点
        //要保证两直线不平行或重合
        Point crosspoint(Line v) {
            double a1 = (v.e - v.s) ^ (s - v.s);
            double a2 = (v.e - v.s) ^ (e - v.s);
            return Point((s.x * a2 - e.x * a1) / (a2 - a1),
                         (s.y * a2 - e.y * a1) / (a2 - a1));
        }
        double length() { return s.dist(e); }
        //点到直线的距离
        double dispointtoline(Point p) {
            return fabs((p - s) ^ (e - s)) / length();
        }
        //点到线段的距离
        double dispointtoseg(Point p) {
            if (sgn((p - s) * (e - s)) < 0 || sgn((p - e) * (s - e)) < 0)
                return min(p.dist(s), p.dist(e));
            return dispointtoline(p);
        }
        //直线和线段相交判断
        //-*this line   -v seg
        // 2 规范相交
        // 1 非规范相交
        // 0 不相交
        int linecrossseg(Line v) {
            int d1 = sgn((e - s) ^ (v.s - s));
            int d2 = sgn((e - s) ^ (v.e - s));
            if ((d1 ^ d2) == -2) return 2;
            return (d1 == 0 || d2 == 0);
        }
    };
    
    struct polygon {
        int n;
        Point p[maxp];
        // 输入多边形
        void input(int _n) {
            n = _n;
            for (int i = 0; i < n; i++) p[i].input();
        }
        // 加入一个点
        void add(Point q) { p[n++] = q; }
        // 得到多边形所有边的线段
    
        // 极角排序
        struct cmp {
            Point p;
            cmp(const Point &p0) { p = p0; }
            bool operator()(const Point &aa, const Point &bb) {
                Point a = aa, b = bb;
                int d = sgn((a - p) ^ (b - p));
                if (d == 0) {
                    return sgn(a.dist(p) - b.dist(p)) < 0;
                }
                return d > 0;
            }
        };
        // 进行极角排序
        // 首先需要找到最左下角的点
        // 需要重载号好Point的 < 操作符(min函数要用)
        void norm() {
            Point mi = p[0];
            for (int i = 1; i < n; i++)
                mi = min(mi, p[i]);  // min函数用Point的<重载
            sort(p, p + n, cmp(mi));
        }
        void Graham(polygon &convex) {
            norm();
            int &top = convex.n;
            top = 0;
            if (n == 1) {
                top = 1;
                convex.p[0] = p[0];
                return;
            }
            if (n == 2) {
                top = 2;
                convex.p[0] = p[0];
                convex.p[1] = p[1];
                if (convex.p[0] == convex.p[1]) top--;
                return;
            }
            convex.p[0] = p[0];
            convex.p[1] = p[1];
            top = 2;
            for (int i = 2; i < n; i++) {
                while (top > 1 && sgn((convex.p[top - 1] - convex.p[top - 2]) ^
                                      (p[i] - convex.p[top - 2])) <= 0)
                    top--;
                convex.p[top++] = p[i];
            }
            if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n--;  //特判
        }
    
        // 得到多边形的周长
        double getcircumference() {
            double sum = 0;
            for (int i = 0; i < n; i++) {
                sum += p[i].dist(p[(i + 1) % n]);
            }
            return sum;
        }
        double getarea() {
            double sum = 0;
            for (int i = 0; i < n; i++) {
                sum += (p[i] ^ p[(i + 1) % n]);
            }
            return fabs(sum) / 2;
        }
    };
    int t, n;
    double v[20], l[20];
    polygon P, tb;
    int main() {
        scanf("%d", &n);
        for (int i = 0; i < n; i++) {
            double x, y;
            scanf("%lf%lf", &x, &y);
            P.add(Point(x, y));
        }
        P.Graham(tb);
        double area=tb.getarea();
        printf("%d
    ", int(area / 50));
    }
    
    
  • 相关阅读:
    使用 Sublime、WebStorm 开发 Jade
    3-微信小程序开发(小程序的目录结构说明)
    1-微信小程序开发(安装软件和运行第一个微信小程序)
    29-ESP8266 SDK开发基础入门篇--编写TCP 客户端程序(Lwip RAW模式,非RTOS版,精简入门)
    28-ESP8266 SDK开发基础入门篇--编写wifi模块TCP 客户端程序(官方API版,非RTOS版)
    27-ESP8266 SDK开发基础入门篇--编写Android SmartConfig一键配网程序
    26-ESP8266 SDK开发基础入门篇--编写WIFI模块 SmartConfig/Airkiss 一键配网
    25-ESP8266 SDK开发基础入门篇--控制WIFI连接路由器
    17-网页,网站,微信公众号基础入门(使用Adobe Dreamweaver CS6 制作网页/网站)
    15-网页,网站,微信公众号基础入门(网页版MQTT,做自己的MQTT调试助手)
  • 原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14131431.html
Copyright © 2011-2022 走看看