• 模板 凸包 旋转卡壳


    模板  凸包  旋转卡壳

     

     

    lrj 《训练指南》 P272

    对于个点按照 x 从小到大排序,再按照 y 点从小到大排序,删除重复的点后,得到序列 p0,p1,p2...,
    把 p0 和 p1 放入凸包。 从p2开始,当新点在凸包“前进”方向的左边时继续,否则依次删除最近加入凸包的点,直到新点在左边
    PS:判断用叉积即可
     
     
     1 /********************************************
     2 计算凸包,输入点数组 p, 个数为 n , 输出点数组 ch. 函数返回凸包顶点数
     3 输入不能有重复点。函数执行完之后输入点的顺序被破坏
     4 如果不希望在凸包的边上有输入点,把两个 <= 改成 < 【== 表示两向量共线】
     5 在精度要求高时建议用 dcmp 比较
     6 
     7 const double eps = 1e-10;
     8 int dcmp(double x)
     9 {
    10     if(fabs(x) < eps) return 0;
    11     else return x < 0 ? -1 : 1;
    12 }
    13 ********************************************/
    14 double ConvexHull(Point* p, int n, Point* ch) /** 基于水平的Andrew算法求凸包 */
    15 {
    16     sort(p,p+n,cmp); /**先按照 x 从小到大排序, 再按照 y 从小到大排序*/
    17     int m = 0;
    18 
    19     for(int i = 0; i < n; i++) /** 从前往后找,求"下凸包" */
    20     {/**Cross <= 0有等于,去掉 重复的点*/
    21         while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
    22         ch[m++] = p[i];
    23     }
    24     int k = m;
    25     for(int i = n-2; i >= 0; i--) /**从后往前找,求"上凸包" 形成完整的封闭背包*/
    26     {
    27         while(m > k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
    28         ch[m++] = p[i];
    29     }
    30     if(n > 1) m--; /** 起点重复 */
    31     return m;
    32 }
    33 
    34 旋转卡壳模板:
    35 
    36 盗版的别人博客的模板,下面会贴上大牛的关于这个算法的分析,一般是能看懂的了,如果不能看懂的,记住就好了
    37 
    38 
    39 int rotating_calipers(Point *ch, int m) /**旋转卡壳模板*/
    40 {
    41     int q = 1;
    42     int ans = 0;
    43     ch[m] = ch[0]; /**凸包边界处理*/
    44     for(int i = 0; i < m; i++) /**依次用叉积找出凸包每一条边对应的最高点*/
    45     {/**同底不同高,每次用面积判断高的大小就可以了*/
    46         while(Cross(ch[i+1]-ch[i], ch[q+1]-ch[i]) > Cross(ch[i+1]-ch[i], ch[q]-ch[i]))
    47             q = (q+1)%m;
    48     /**每条底上有两个点,所以要 max 两次*/
    49         ans = max(ans, max(squarDist(ch[i], ch[q]), squarDist(ch[i+1], ch[q+1])));
    50     }
    51     return ans;/**返回的也就是凸包的直径*/
    52 }

    思路:

    暴力:最远距离两个点一定在凸包上,建立好背包后,枚举凸包上的点就可以了
    对于凸包上的点很多,暴力的话肯定是不行的了,那么就用旋转卡壳,只是名字听着神奇,其实也很好理解了
    旋转卡壳:直接套模板,求直径
     

    code1:暴力+凸包【对应于凸包上点比较少】

     1 /********************************************
     2 题意:给你 N 个点, 求所有点中最远两点距离
     3 算法:凸包+暴力
     4 思路:最远距离两个点一定在凸包上,建立好背包后,枚举凸包上的点就可以了
     5 *********************************************/
     6 #include<stdio.h>
     7 #include<math.h>
     8 #include<string.h>
     9 #include<algorithm>
    10 using namespace std;
    11 
    12 const int maxn = 50000+10;
    13 int n,m;
    14 
    15 struct Point{
    16     double x,y;
    17     Point(){};
    18     Point(double _x, double _y)
    19     {
    20         x = _x;
    21         y = _y;
    22     }
    23 
    24     Point operator - (const Point & B) const
    25     {
    26         return Point(x-B.x, y-B.y);
    27     }
    28 }p[maxn], ch[maxn];
    29 
    30 bool cmp(Point p1, Point p2)
    31 {
    32     if(p1.x == p2.x) return p1.y < p2.y;
    33     return p1.x < p2.x;
    34 }
    35 
    36 int squarDist(Point A, Point B) /**距离的平方*/
    37 {
    38     return (A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y);
    39 }
    40 
    41 double Cross(Point A, Point B) /**叉积*/
    42 {
    43     return A.x*B.y-A.y*B.x;
    44 }
    45 
    46 void ConvexHull() /** 求凸包 */
    47 {
    48     sort(p,p+n,cmp); /**先按照 x 从小到大排序, 再按照 y 从小到大排序*/
    49     m = 0;
    50 
    51     for(int i = 0; i < n; i++) /** 从前往后找"下凸包" */
    52     {
    53         while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
    54         ch[m++] = p[i];
    55     }
    56     int k = m;
    57     for(int i = n-2; i >= 0; i--) /**从后往前找"上凸包", 形成完整的封闭背包*/
    58     {
    59         while(m > k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
    60         ch[m++] = p[i];
    61     }
    62     if(n > 1) m--; /** 起点重复*/
    63 }
    64 
    65 int main()
    66 {
    67     while(scanf("%d", &n) != EOF)
    68     {
    69         if(n == 0) break;
    70         for(int i = 0; i < n; i++)
    71             scanf("%lf%lf", &p[i].x, &p[i].y);
    72 
    73         ConvexHull();
    74         int ans = 0;
    75         for(int i = 0; i < m; i++)
    76             for(int j = i+1; j < m; j++)
    77                 ans = max(ans,squarDist(ch[i], ch[j]));
    78         printf("%d
    ", ans);
    79     }
    80     return 0;
    81 }
    View Code

     

     

    code2:凸包+旋转卡壳【对于凸包上点比较多】

     
     1 /********************************************
     2 2187    Accepted    972K    297MS   C++ 1927B   2013-07-27 13:38:35
     3 题意:给你 N 个点, 求所有点中最远两点距离
     4 算法:凸包+旋转卡壳
     5 思路:最远距离两个点一定在凸包上,建立好背包后,直接套用旋转卡壳找直径
     6 *********************************************/
     7 #include<stdio.h>
     8 #include<math.h>
     9 #include<string.h>
    10 #include<algorithm>
    11 using namespace std;
    12 
    13 const int maxn = 50000+10;
    14 int n,m;
    15 
    16 struct Point{
    17     double x,y;
    18     Point(){};
    19     Point(double _x, double _y)
    20     {
    21         x = _x;
    22         y = _y;
    23     }
    24 
    25     Point operator - (const Point & B) const
    26     {
    27         return Point(x-B.x, y-B.y);
    28     }
    29 }p[maxn], ch[maxn];
    30 
    31 bool cmp(Point p1, Point p2)
    32 {
    33     if(p1.x == p2.x) return p1.y < p2.y;
    34     return p1.x < p2.x;
    35 }
    36 
    37 int squarDist(Point A, Point B) /**距离的平方*/
    38 {
    39     return (A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y);
    40 }
    41 
    42 double Cross(Point A, Point B) /**叉积*/
    43 {
    44     return A.x*B.y-A.y*B.x;
    45 }
    46 
    47 void ConvexHull() /** 基于水平的Andrew算法求凸包 */
    48 {
    49     sort(p,p+n,cmp); /**先按照 x 从小到大排序, 再按照 y 从小到大排序*/
    50     m = 0;
    51 
    52     for(int i = 0; i < n; i++) /** 从前往后找 */
    53     {
    54         while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
    55         ch[m++] = p[i];
    56     }
    57     int k = m;
    58     for(int i = n-2; i >= 0; i--) /**从后往前找, 形成完整的封闭背包*/
    59     {
    60         while(m > k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
    61         ch[m++] = p[i];
    62     }
    63     if(n > 1) m--;
    64 }
    65 
    66 int rotating_calipers() /**旋转卡壳模板*/
    67 {
    68     int q = 1;
    69     int ans = 0;
    70     ch[m] = ch[0]; /**凸包边界处理*/
    71     for(int i = 0; i < m; i++) /**依次用叉积找出凸包每一条边对应的最高点*/
    72     {
    73         while(Cross(ch[i+1]-ch[i], ch[q+1]-ch[i]) > Cross(ch[i+1]-ch[i], ch[q]-ch[i]))
    74             q = (q+1)%m;
    75         ans = max(ans, max(squarDist(ch[i], ch[q]), squarDist(ch[i+1], ch[q+1])));
    76     }
    77     return ans;
    78 }
    79 
    80 int main()
    81 {
    82     while(scanf("%d", &n) != EOF)
    83     {
    84         if(n == 0) break;
    85         for(int i = 0; i < n; i++)
    86             scanf("%lf%lf", &p[i].x, &p[i].y);
    87 
    88         ConvexHull();
    89 
    90         printf("%d
    ", rotating_calipers());
    91     }
    92     return 0;
    93 }
    View Code

     另外还有关于凸包周长的题目

  • 相关阅读:
    偶遇this之坑
    程序员的职业素养——读后感
    我怎么没想到——读后感
    黑客与画家——读后感
    漫谈认证与授权
    动手造轮子:实现一个简单的依赖注入(二) --- 服务注册优化
    动手造轮子:实现简单的 EventQueue
    asp.net core 自定义 Policy 替换 AllowAnonymous 的行为
    SQL Server 中 `JSON_MODIFY` 的使用
    WeihanLi.Npoi 近期更新
  • 原文地址:https://www.cnblogs.com/jeff-wgc/p/4474771.html
Copyright © 2020-2023  润新知