POJ 2187 Beauty Contest【旋转卡壳求凸包直径】
链接:
| Time Limit: 3000MS | Memory Limit: 65536K | |
| Total Submissions: 24254 | Accepted: 7403 |
Description
cows. For simplicity, the world will be represented as a two-dimensional plane, where each farm is located at a pair of integer coordinates (x,y), each having a value in the range -10,000 ... 10,000. No two farms share the same pair of coordinates.
Even though Bessie travels directly in a straight line between pairs of farms, the distance between some farms can be quite large, so she wants to bring a suitcase full of hay with her so she has enough food to eat on each leg of her journey. Since Bessie refills
her suitcase at every farm she visits, she wants to determine the maximum possible distance she might need to travel so she knows the size of suitcase she must bring.Help Bessie by computing the maximum distance among all pairs of farms.
Input
* Lines 2..N+1: Two space-separated integers x and y specifying coordinate of each farm
Output
Sample Input
4
0 0
0 1
1 1
1 0
Sample Output
2
Hint
Source
题意:
算法:
建立凸包:
凸包模板:
/********************************************
计算凸包,输入点数组 p, 个数为 n , 输出点数组 ch. 函数返回凸包顶点数
输入不能有重复点。函数执行完之后输入点的顺序被破坏
如果不希望在凸包的边上有输入点,把两个 <= 改成 < 【== 表示两向量共线】
在精度要求高时建议用 dcmp 比较 const double eps = 1e-10;
int dcmp(double x)
{
if(fabs(x) < eps) return 0;
else return x < 0 ? -1 : 1;
}
********************************************/
double ConvexHull(Point* p, int n, Point* ch) /** 基于水平的Andrew算法求凸包 */
{
sort(p,p+n,cmp); /**先按照 x 从小到大排序, 再按照 y 从小到大排序*/
int m = 0; for(int i = 0; i < n; i++) /** 从前往后找,求"下凸包" */
{/**Cross <= 0有等于,去掉 重复的点*/
while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 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-2]) <= 0) m--;
ch[m++] = p[i];
}
if(n > 1) m--; /** 起点重复 */
return m;
}
旋转卡壳模板:
盗版的别人博客的模板,下面会贴上大牛的关于这个算法的分析,一般是能看懂的了,如果不能看懂的,记住就好了
int rotating_calipers(Point *ch, int m) /**旋转卡壳模板*/
{
int q = 1;
int ans = 0;
ch[m] = ch[0]; /**凸包边界处理*/
for(int i = 0; i < m; i++) /**依次用叉积找出凸包每一条边对应的最高点*/
{/**同底不同高,每次用面积判断高的大小就可以了*/
while(Cross(ch[i+1]-ch[i], ch[q+1]-ch[i]) > Cross(ch[i+1]-ch[i], ch[q]-ch[i]))
q = (q+1)%m;
/**每条底上有两个点,所以要 max 两次*/
ans = max(ans, max(squarDist(ch[i], ch[q]), squarDist(ch[i+1], ch[q+1])));
}
return ans;/**返回的也就是凸包的直径*/
}
思路:
code1:暴力+凸包【对应于凸包上点比较少】
/********************************************
题意:给你 N 个点, 求所有点中最远两点距离
算法:凸包+暴力
思路:最远距离两个点一定在凸包上,建立好背包后,枚举凸包上的点就可以了
*********************************************/
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std; const int maxn = 50000+10;
int n,m; struct Point{
double x,y;
Point(){};
Point(double _x, double _y)
{
x = _x;
y = _y;
} Point operator - (const Point & B) const
{
return Point(x-B.x, y-B.y);
}
}p[maxn], ch[maxn]; bool cmp(Point p1, Point p2)
{
if(p1.x == p2.x) return p1.y < p2.y;
return p1.x < p2.x;
} int squarDist(Point A, Point B) /**距离的平方*/
{
return (A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y);
} double Cross(Point A, Point B) /**叉积*/
{
return A.x*B.y-A.y*B.x;
} void ConvexHull() /** 求凸包 */
{
sort(p,p+n,cmp); /**先按照 x 从小到大排序, 再按照 y 从小到大排序*/
m = 0; for(int i = 0; i < n; i++) /** 从前往后找"下凸包" */
{
while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 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-2]) <= 0) m--;
ch[m++] = p[i];
}
if(n > 1) m--; /** 起点重复*/
} int main()
{
while(scanf("%d", &n) != EOF)
{
if(n == 0) break;
for(int i = 0; i < n; i++)
scanf("%lf%lf", &p[i].x, &p[i].y); ConvexHull();
int ans = 0;
for(int i = 0; i < m; i++)
for(int j = i+1; j < m; j++)
ans = max(ans,squarDist(ch[i], ch[j]));
printf("%d\n", ans);
}
return 0;
}
code2:凸包+旋转卡壳【对于凸包上点比较多】
/********************************************
2187 Accepted 972K 297MS C++ 1927B 2013-07-27 13:38:35
题意:给你 N 个点, 求所有点中最远两点距离
算法:凸包+旋转卡壳
思路:最远距离两个点一定在凸包上,建立好背包后,直接套用旋转卡壳找直径
*********************************************/
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std; const int maxn = 50000+10;
int n,m; struct Point{
double x,y;
Point(){};
Point(double _x, double _y)
{
x = _x;
y = _y;
} Point operator - (const Point & B) const
{
return Point(x-B.x, y-B.y);
}
}p[maxn], ch[maxn]; bool cmp(Point p1, Point p2)
{
if(p1.x == p2.x) return p1.y < p2.y;
return p1.x < p2.x;
} int squarDist(Point A, Point B) /**距离的平方*/
{
return (A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y);
} double Cross(Point A, Point B) /**叉积*/
{
return A.x*B.y-A.y*B.x;
} void ConvexHull() /** 基于水平的Andrew算法求凸包 */
{
sort(p,p+n,cmp); /**先按照 x 从小到大排序, 再按照 y 从小到大排序*/
m = 0; for(int i = 0; i < n; i++) /** 从前往后找 */
{
while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 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-2]) <= 0) m--;
ch[m++] = p[i];
}
if(n > 1) m--;
} int rotating_calipers() /**旋转卡壳模板*/
{
int q = 1;
int ans = 0;
ch[m] = ch[0]; /**凸包边界处理*/
for(int i = 0; i < m; i++) /**依次用叉积找出凸包每一条边对应的最高点*/
{
while(Cross(ch[i+1]-ch[i], ch[q+1]-ch[i]) > Cross(ch[i+1]-ch[i], ch[q]-ch[i]))
q = (q+1)%m;
ans = max(ans, max(squarDist(ch[i], ch[q]), squarDist(ch[i+1], ch[q+1])));
}
return ans;
} int main()
{
while(scanf("%d", &n) != EOF)
{
if(n == 0) break;
for(int i = 0; i < n; i++)
scanf("%lf%lf", &p[i].x, &p[i].y); ConvexHull(); printf("%d\n", rotating_calipers());
}
return 0;
}
旋转卡壳算法分析:
好早以前看的,现在再记下来吧,当做复习一遍。
那么,先提一下最基本最暴力的求凸包直径的方法吧---枚举。。。好吧。。很多问题都可以用 枚举 这个“万能”的方法来解决,过程很简单方便是肯定的,不过在效率上就要差很远了。 要求一个点集的直径,即使先计算出这个点集的凸包,然后再枚举凸包上的点对,这样来求点集直径的话依然会在凸包上点的数量达到O(n)级别是极大的降低它的效率,也浪费了凸包的优美性质。不过在数据量较小或者很适合时,何必要大费周折的用那些麻烦复杂的算法呢,枚举依然是解决问题的很好的方法之一。
然后就是今天的旋转卡壳算法了。(那个字念 qia 。。。搞了好久才发现读都读错了。囧。。。)
旋转卡壳可以用于求凸包的直径、宽度,两个不相交凸包间的最大距离和最小距离等。虽然算法的思想不难理解,但是实现起来真的很容易让人“卡壳”。

其实简单来说就是用一对平行线“卡”住凸包进行旋转。
被一对卡壳正好卡住的对应点对称为对踵点,对锺点的具体定义不好说,不过从图上还是比较好理解的。
可以证明对踵点的个数不超过3N/2个 也就是说对踵点的个数是O(N)的
对踵点的个数也是我们下面解决问题时间复杂度的保证。
卡壳呢,具体来说有两种情况:
1.
一种是这样,两个平行线正好卡着两个点;
2.
一种是这样,分别卡着一条边和一个点。
而第二种情况在实现中比较容易处理,这里就只研究第二种情况。
在第二种情况中 我们可以看到 一个对踵点和对应边之间的距离比其他点要大(借用某大神的图··)
也就是一个对踵点和对应边所形成的三角形是最大的 下面我们会据此得到对踵点的简化求法。

下面给出一个官方的说明:
Compute the polygon's extreme points in the y direction. Call them ymin and ymax. Construct two horizontal lines of support through ymin and ymax. Since this is already an anti-podal pair, compute the distance, and keep as maximum. Rotate the lines until one
is flush with an edge of the polygon. A new anti-podal pair is determined. Compute the new distance, compare to old maximum, and update if necessary. Repeat steps 3 and 4 until the anti-podal pair considered is (ymin,ymax) again. Output the pair(s) determining
the maximum as the diameter pair(s).

要是真的按这个实现起来就麻烦到吐了。。
根据上面的第二种情况,我们可以得到下面的方法:
如果qa,qb是凸包上最远两点,必然可以分别过qa,qb画出一对平行线。通过旋转这对平行线,我们可以让它和凸包上的一条边重合,如图中蓝色直线,可以注意到,qa是凸包上离p和qb所在直线最远的点。于是我们的思路就是枚举凸包上的所有边,对每一条边找出凸包上离该边最远的顶点,计算这个顶点到该边两个端点的距离,并记录最大的值。
直观上这是一个O(n2)的算法,和直接枚举任意两个顶点一样了。
然而我们可以发现 凸包上的点依次与对应边产生的距离成单峰函数(如下图:)

这个性质就很重要啦。
根据这个凸包的特性,我们注意到逆时针枚举边的时候,最远点的变化也是逆时针的,这样就可以不用从头计算最远点,而可以紧接着上一次的最远点继续计算。于是我们得到了O(n)的算法。这就是所谓的“旋转”吧!
利用旋转卡壳,我们可以在O(n)的时间内得到凸包的对锺点中的长度最长的点对。
又由于最远点对必然属于对踵点对集合 ,那么我们先求出凸包 然后求出对踵点对集合 然后选出距离最大的即可
那么具体的代码就很容易实现了,利用叉积,代码只有这么几行的长度:
double rotating_calipers(Point *ch,int n)
{
int q=1;
double ans=0;
ch[n]=ch[0];
for(int p=0;p<n;p++)
{
while(cross(ch[p+1],ch[q+1],ch[p])>cross(ch[p+1],ch[q],ch[p]))
q=(q+1)%n;
ans=max(ans,max(dist(ch[p],ch[q]),dist(ch[p+1],ch[q+1])));
}
return ans;
}
其中cross()是计算叉积,因为凸包上距离一条边最远的点和这条边的两个端点构成的三角形面积是最大的。之所以既要更新(ch[p],ch[q])又要更新(ch[p+1],ch[q+1])是为了处理凸包上两条边平行的特殊情况。
POJ 2187 Beauty Contest【旋转卡壳求凸包直径】的更多相关文章
- poj 2187 Beauty Contest , 旋转卡壳求凸包的直径的平方
旋转卡壳求凸包的直径的平方 板子题 #include<cstdio> #include<vector> #include<cmath> #include<al ...
- poj 2187:Beauty Contest(旋转卡壳)
Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 32708 Accepted: 10156 Description Bes ...
- poj 2187 Beauty Contest——旋转卡壳
题目:http://poj.org/problem?id=2187 学习材料:https://blog.csdn.net/wang_heng199/article/details/74477738 h ...
- poj 2187 Beauty Contest —— 旋转卡壳
题目:http://poj.org/problem?id=2187 学习资料:https://blog.csdn.net/wang_heng199/article/details/74477738 h ...
- UVa 1453 - Squares 旋转卡壳求凸包直径
旋转卡壳求凸包直径. 参考:http://www.cppblog.com/staryjy/archive/2010/09/25/101412.html #include <cstdio> ...
- poj 2187 Beauty Contest(凸包求解多节点的之间的最大距离)
/* poj 2187 Beauty Contest 凸包:寻找每两点之间距离的最大值 这个最大值一定是在凸包的边缘上的! 求凸包的算法: Andrew算法! */ #include<iostr ...
- POJ 2187 - Beauty Contest - [凸包+旋转卡壳法][凸包的直径]
题目链接:http://poj.org/problem?id=2187 Time Limit: 3000MS Memory Limit: 65536K Description Bessie, Farm ...
- POJ 2187 Beauty Contest(凸包,旋转卡壳)
题面 Bessie, Farmer John's prize cow, has just won first place in a bovine beauty contest, earning the ...
- POJ 2187 Beauty Contest(凸包+旋转卡壳)
Description Bessie, Farmer John's prize cow, has just won first place in a bovine beauty contest, ea ...
随机推荐
- Java 7 新功能: 省略finally, 保证资源正常关闭
class MyResource implements Closeable{ @Override public void close() throw IOException{ } } try( myR ...
- PHP Warning: 的解决方法
在后台管理,用header("location:");做返回时,总是不能正常返回, Warning: Cannot modify header information - head ...
- Elasticsearch教程(八) elasticsearch delete 删除数据(Java)
Elasticsearch的删除也是很灵活的,下次我再介绍,DeleteByQuery的方式.今天就先介绍一个根据ID删除.上代码. package com.sojson.core.elasticse ...
- Linux Barrier I/O 实现分析与barrier内存屏蔽 总结
一直以来.I/O顺序问题一直困扰着我.事实上这个问题是一个比較综合的问题,它涉及的层次比較多,从VFS page cache到I/O调度算法,从i/o子系统到存储外设.而Linux I/O barri ...
- 使用终端shell命令批量改动一个文件下的全部文件的读写权限
之前对openfire安装的目录就遇到过这个问题,今天再次遇到.须要改动一个目录以下的全部子目录以及文件的三个权限:本用户读写.管理员读写.全部人读写,三个都要需改为wr 步骤例如以下:比如我要改动/ ...
- 关于Laravel5.2在php5.3.6X和在php7.1.10下的内存溢出
php5.3.6X是编译安装,在debug模式下,频繁报出内存泄露警告 php7.1.10下则不会有此错误. 顺便提下:测试发现ThinkPHP也不会有该内存泄露警告! 希望知道如何解决该问题的童鞋能 ...
- java 实现新浪微博内容计数器 Java问题通用解决代码
http://www.mr3g.net/?p=220 参考sina的js版本而来,费弄最多的时间就是java对ansii码的判断了,js直接就是isascii()函数就可以实现了,java还要想办法 ...
- CSS 选择器 :last-child与:last-of-type的区别
:last-child----represents the last element among a group of sibling elements. CSS伪类 :last-child 代表在一 ...
- MySQL:ERROR 1067 (42000): Invalid default value for 'end_time'
© 版权声明:本文为博主原创文章,转载请注明出处 1.错误截图 2.错误分析 表中的第一个TIMESTAMP列(如果未声明为NULL或显示DEFAULT或ON UPDATE子句)将自动分配DEFAUL ...
- 模拟和数字低通滤波器的MATLAB实现
低通滤波器参数:Fs=8000,fp=2500,fs=3500,Rp=1dB,As=30dB,其他滤波器可以通过与低通之间的映射关系实现. %%模拟滤波器 %巴特沃斯——滤波器设计 wp=2*pi*2 ...