coding is a rhythm game

algorithm is crying

旋转卡壳算法 poj2187 poj3608

    旋转卡壳可以用于求凸包的直径、宽度,两个不相交凸包间的最大距离和最小距离等。虽然算法的思想不难理解,但是实现起来真的很容易让人“卡壳”。
   拿凸包直径(也就是凸包上最远的两点的距离)为例,原始的算法是这样子:
  
      

  1. Compute the polygon's extreme points in the y direction. Call them ymin and ymax.
  2. 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.
  3. Rotate the lines until one is flush with an edge of the polygon.
  4. A new anti-podal pair is determined. Compute the new distance, compare to old maximum, and update if necessary.
  5. Repeat steps 3 and 4 until the anti-podal pair considered is (ymin,ymax) again.
  6. Output the pair(s) determining the maximum as the diameter pair(s).

   更具体的可参见http://cgm.cs.mcgill.ca/~orm/rotcal.frame.html
      

   直接按照这个描述可以实现旋转卡壳算法,但是代码肯定相当冗长。逆向思考,如果qa,qb是凸包上最远两点,必然可以分别过qa,qb画出一对平行线。通过旋转这对平行线,我们可以让它和凸包上的一条边重合,如图中蓝色直线,可以注意到,qa是凸包上离p和qb所在直线最远的点。于是我们的思路就是枚举凸包上的所有边,对每一条边找出凸包上离该边最远的顶点,计算这个顶点到该边两个端点的距离,并记录最大的值。直观上这是一个O(n2)的算法,和直接枚举任意两个顶点一样了。但是注意到当我们逆时针枚举边的时候,最远点的变化也是逆时针的,这样就可以不用从头计算最远点,而可以紧接着上一次的最远点继续计算(详细的证明可以参见上面链接中的论文)。于是我们得到了O(n)的算法。

//计算凸包直径,输入凸包ch,顶点个数为n,按逆时针排列,输出直径的平方
int rotating_calipers(Point *ch,int n)
{
    
int q=1,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(dist2(ch[p],ch[q]),dist2(ch[p+1],ch[q+1])));            
    }

    
return ans; 
}

   很难想象这个看起来那么麻烦的算法只有这么几行代码吧!其中cross函数是计算叉积,可以想成是计算三角形面积,因为凸包上距离一条边最远的点和这条边的两个端点构成的三角形面积是最大的。之所以既要更新(ch[p],ch[q])又要更新(ch[p+1],ch[q+1])是为了处理凸包上两条边平行的特殊情况。

   poj2187要求的是平面点集上的最远点对,实际上就是该点集的凸包的直径。可能该题数据求得的凸包顶点数都不多,所以旋转卡壳算法相比普通的枚举算法并没有明显的优势。完整代码如下。

poj2187

   poj3608 要求的是两个凸包的最近距离。这比求凸包直径麻烦了许多。我的基本思想还是分别枚举两个凸包的边,但是有些细节没能完全证明是正确的。虽然AC了,但目前这还只是一个看起来正确的算法。这题的中间过程还需要计算点到线段的距离和两条平行线段的距离,比起2187麻烦了许多。

poj3608


 

 

posted on 2009-11-19 20:28 liam 阅读(10525) 评论(19)  编辑 收藏 引用

评论

# re: 旋转卡壳算法 poj2187 poj3608 2009-11-20 18:59 99读书人

斯蒂芬斯蒂芬  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2009-11-22 14:22 罗莱价格

是束带结发  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2010-02-28 21:25 joy32812

写的灰常好,受教了!!
顶LZ!  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2010-02-28 23:35 joy32812

//计算凸包直径,输入凸包ch,顶点个数为n,按逆时针排列,输出直径的平方
int rotating_calipers(Point *ch,int n)
{
int q=1,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(dist2(ch[p],ch[q]),dist2(ch[p+1],ch[q+1])));
}
return ans;
}
*************************************************
想问下LZ,这段代码中cross部分的面积比较;
有可能出现面积为负的情况吧。。
为什么不是fabs(cross())呢??(我测试下,加上fabs是错误的)
谢~~
  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2010-03-01 09:20 liam

@joy32812
不会是负的哦,因为规定了输入是逆时针排列的
可能因为cross()返回值是整形,应该使用abs()吧  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2010-04-10 14:33 ccsu_010

学习了,帮你顶一下!  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2010-04-14 23:06 lc

强顶。~~  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2010-08-11 23:11 asd

cross第几个参数是作为参考点的  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2010-08-22 10:45 小志

顶一下!l理论上,还是可以对上面的过程进行优化的!因为,旋转一圈进行了一些重复的比较!期待大牛的更新!!!!  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2010-09-07 19:23 mingrimingyue

我写了一上午的旋转卡壳啊~~~这么简单就实现了~~~膜拜~~~~  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2010-09-25 22:38 OWenT

很简洁很不错。学习了  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2010-12-24 17:11 otis

嘛,受益匪浅  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2011-02-28 17:58 ch_g

ans=max(ans,max(dist2(ch[p],ch[q]),dist2(ch[p+1],ch[q+1])));
这句可以改成
ans=max(ans,dist2(ch[p],ch[q])); 吗?
感觉dist2(ch[p+1],ch[q+1])会在之后的算到的  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2011-02-28 18:02 ch_g

不好意思是我弄错了
  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608[未登录] 2011-05-17 15:33 cc

len=top
ch[top]=ch[0]
应该换成ch[top+1]=ch[0]吧;
要是直接赋值的话,你就把最后一个凸包上的点弄没了。  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2011-05-31 22:47 李翼龙

请问楼主2187 里面求凸包上的点为什么还要反过来弄一次  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2013-02-15 19:16 pro

@李翼龙
求一次上凸壳和下凸壳  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608 2013-07-20 01:35 J

拜托,理论都错了
谁说离一条边最远的点是围成面积最大的点?
随便画个菱形就知道错了  回复  更多评论   

# re: 旋转卡壳算法 poj2187 poj3608[未登录] 2014-04-13 09:19 x

@J
我笑了。。LS初中数学怎么学的。。  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理