模板 凸包 旋转卡壳
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 }
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 }