poj 2187 Beauty Contest 计算几何(凸包+旋转卡壳法)/最远点对
2014-07-17 11:13
393 查看
题意:给定n(2<=n<=50000)对整数点,求距离最远的一对点(即最远点对)的距离的平方。
题解:
1)先确定最远点对必定是凸包上的一对点。对于一个三个点组成的三角形,其内部任意点肯定不属于最远点对。证明如下:
*这个点到三角形三点必定比最长的三角形边短。
*这个点A与与三角形外部点B相连时,过这个点作连线的垂线,垂线必定与三角形有交点,即在BA方向,垂线外必定存在一个三角形上的点C,使得BC>AB。
而在凸包中,任意点必定可以找到一个由凸包上的点构成的三角形,使得该点在三角形内(对于m凸包,可以分成m-2个三角形,覆盖整个凸包)。
2)通过旋转卡壳法找到最远点对。
旋转卡壳法就是模拟游标卡尺旋转凸包的方法,先随意卡住两点,然后旋转,当找到更远的点对时,卡尺会被撑开,旋转一周后,卡尺的宽度就是最远点对的距离。(什么是游标卡尺?自己百度。。)
详细的解释:
过最远的点对作连线的垂线,两条线平行,且不与凸包上有交点(如果有交点,就说明存在更远的点,那么最远点对就不成立)。反过来说就是所有可以作这两条不
与凸包相交平行垂线的点对中必定存在最远点对。现在就是要枚举这些可以作平行垂线的点对即可。而且由上述解释可以得出,每个点相对应这个点的最远点有且只有一
个,那么枚举的时候就可以只枚举一半的点就可以了。
现在就是找必定可以作平行垂线的两个点(例如:横坐标最小和最大的两个点),然后两条线平行逆时针旋转,便利所有点,就可以得到所有点对了。在旋转过程中,
只有平行线与凸包的边相重合才会变换点,所以可以用叉积确定先过那条边Cross(ch[(i+1)%m]-ch[i],ch[(j+1)%m]-ch[j])<0(通过叉积正负,我们可以知道那条平线先到边
上,从而变化对应点。)
代码:
题解:
1)先确定最远点对必定是凸包上的一对点。对于一个三个点组成的三角形,其内部任意点肯定不属于最远点对。证明如下:
*这个点到三角形三点必定比最长的三角形边短。
*这个点A与与三角形外部点B相连时,过这个点作连线的垂线,垂线必定与三角形有交点,即在BA方向,垂线外必定存在一个三角形上的点C,使得BC>AB。
而在凸包中,任意点必定可以找到一个由凸包上的点构成的三角形,使得该点在三角形内(对于m凸包,可以分成m-2个三角形,覆盖整个凸包)。
2)通过旋转卡壳法找到最远点对。
旋转卡壳法就是模拟游标卡尺旋转凸包的方法,先随意卡住两点,然后旋转,当找到更远的点对时,卡尺会被撑开,旋转一周后,卡尺的宽度就是最远点对的距离。(什么是游标卡尺?自己百度。。)
详细的解释:
过最远的点对作连线的垂线,两条线平行,且不与凸包上有交点(如果有交点,就说明存在更远的点,那么最远点对就不成立)。反过来说就是所有可以作这两条不
与凸包相交平行垂线的点对中必定存在最远点对。现在就是要枚举这些可以作平行垂线的点对即可。而且由上述解释可以得出,每个点相对应这个点的最远点有且只有一
个,那么枚举的时候就可以只枚举一半的点就可以了。
现在就是找必定可以作平行垂线的两个点(例如:横坐标最小和最大的两个点),然后两条线平行逆时针旋转,便利所有点,就可以得到所有点对了。在旋转过程中,
只有平行线与凸包的边相重合才会变换点,所以可以用叉积确定先过那条边Cross(ch[(i+1)%m]-ch[i],ch[(j+1)%m]-ch[j])<0(通过叉积正负,我们可以知道那条平线先到边
上,从而变化对应点。)
代码:
#include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <iostream> #include <algorithm> #include <map> #include <set> #include <queue> using namespace std; //基础点和向量运算 struct Point{ int x,y; Point(int x=0,int y=0):x(x),y(y){} }; typedef Point Vector; Vector operator + (Vector A,Vector B){return Vector(A.x+B.x,A.y+B.y);} Vector operator - (Vector A,Vector B){return Vector(A.x-B.x,A.y-B.y);} Vector operator * (Vector A,double p){return Vector(A.x*p,A.y*p);} Vector operator / (Vector A,double p){return Vector(A.x/p,A.y/p);} bool operator <(const Point& a, const Point& b) { return a.x<b.x||(a.x==b.x&&a.y<b.y); } int Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;}//叉积 int dis(Vector A,Vector B)//距离的平方 { return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y); } const int maxn=5e4+10; int n; Point p[maxn],ch[maxn]; int ConvexHull()//求凸包 { sort(p,p+n); int i,m=0,k; for(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]; } k=m; for(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; } void solve(int m)//用旋转卡壳法求最长点对 { if(m==2) { printf("%d\n",dis(ch[0],ch[1])); return; } int i,j,k; i=j=0; for(k=0;k<m;k++) { if(ch[i].x>ch[k].x)i=k; if(ch[j].x<ch[k].x)j=k; } int res=0,si=i,sj=j; //printf("%d %d\n",i,j); while(i!=sj||j!=si) { res=max(res,dis(ch[i],ch[j])); if(Cross(ch[(i+1)%m]-ch[i],ch[(j+1)%m]-ch[j])<0)i=(i+1)%m; else j=(j+1)%m; } printf("%d\n",res); } int main() { while(scanf("%d",&n)!=EOF) { int i,j,k,m; for(i=0;i<n;i++) scanf("%d%d",&p[i].x,&p[i].y); m=ConvexHull(); //for(i=0;i<m;i++) // printf("*%d %d\n",ch[i].x,ch[i].y); solve(m); } return 0; }
相关文章推荐
- POJ 2187 Beauty Contest [旋转卡壳]【计算几何】
- poj 2187 Beauty Contest (计算几何)
- POJ 2187 Beauty Contest(计算几何-- 凸包)
- 【计算几何】 Andrew凸包算法 + 旋转卡壳(以求点对最长距离为例) -- 以 POJ 2187 Beauty Contest 为例
- POJ 2187(计算几何+旋转卡壳法或暴力)
- poj 2187 计算几何入门题 凸包
- poj 2187:Beauty Contest(计算几何,求凸包,最远点对)
- 简单几何(凸包) POJ 2187 Beauty Contest
- POJ训练计划2187_Beauty Contest(几何/凸包)
- POJ 2187 计算几何之旋转卡壳
- POJ 2187 Beauty Contest ——计算几何
- [Poj 2187] 计算几何之凸包(二) {更高效的算法}
- [Poj 2187]计算几何之凸包(三) {旋转卡壳初步}
- POJ 2187--Beauty Contest
- poj 2187 Beauty Contest(凸包+旋转卡壳)
- POJ 1244 Slots of Fun(计算几何)
- POJ1269(KB13-D 计算几何)
- poj2187 Beauty Contest
- POJ 1039:Pipe 计算几何
- poj1039 Pipe(计算几何叉积求交点)