码迷,mamicode.com
首页 > 其他好文 > 详细

模板 凸包 旋转卡壳

时间:2015-05-03 22:07:38      阅读:145      评论:0      收藏:0      [点我收藏+]

标签:

模板  凸包  旋转卡壳

 

 

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\n", 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\n", rotating_calipers());
91     }
92     return 0;
93 }
View Code

 

 

 

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

 

 

 

模板 凸包 旋转卡壳

标签:

原文地址:http://www.cnblogs.com/jeff-wgc/p/4474771.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!