【POJ 2187】如何修改Graham算法,才能使之顺利解决三点一条直线的问题。

暑期集训中做了 POJ 2187 题,噩梦一场,但是这次仔细把一些 Graham 算法在处理存在三点在一条直线上问题的改进方法找到了。

题目意思很明确:给你平面上的一坨点,求最远两点距离的平方。

思路很容易,很多的解题报告上都有阐述,仅两步:1. 求凸包; 2. 旋转卡壳。


我们都知道,Graham 算法是二维凸包的一种相当好写的算法。但是当有三点一线的情况,此种算法就有可能出现问题。这个坑,在我看来,相当难查。

先贴一段 Graham 算法的代码。

bool cmp(point a, point b)
{
    return direction(p[0], a, b) < 0;
}

void getConvex()
{
    swap(p[0], p[Basis]);
    Basis = 0;
    sort(p + 1, p + n, cmp);
    
    Top = 0;
    p[n] = p[0];
    convex[0] = p[0];
    convex[1] = p[1];
    if (n == 2)
        return;
    Top = 1;
    for (int i = 2; i <= n; i++)
    {
        while (Top > 0 && direction(convex[Top - 1], convex[Top], p[i]) >= 0)
        {
            Top--;
        }
        
        convex[++Top] = p[i];
    }
}

当此代码遇到有三点一线的情况,会出现什么问题呢?

先看下图,比较和谐的情况。

排序后按极角从小到达给编上号。首先 0,1 入栈  然后 2 入栈后由于 0-1-2 方向不是向左,于是将 1 弹出

再看一个不那么尽如人意的例子。我们知道,快速排序对于两个关键字相等的元素,其排序后的先后顺序是不可预知的。很有可能出现以下情况。

注意此时 0 1 入栈,按顺序 0 - 1 - 2 判断 显然 0 - 1 - 2 不是左转(用上述 cmp 函数判断为直线)。于是,万恶就此开始。1 被抛弃了。

显然这样凸包就求错了。

两种解决方案:1. 改用 Jarvis 步进法;2. 改进 cmp 函数。

改进 cmp 函数的方法是这样的:极角相同时,认为离基准点近的小,排在前面。

于是一切情况就转为和谐情况。并且此凸包上包含的点一定是最少的。

很多解题报告改变了弹栈的条件。直线情况不弹栈。我认为这样凸包上的点会略多,对后续的操作显得有些累赘了。


极角排序代码如下:

bool cmp(point a, point b)
{
    if (direction(p[0], a, b) == 0)
        return SQ(a - p[0]) < SQ(b - p[0]);
    
    return direction(p[0], a, b) < 0;
}
SQ 代表向量的模的平方。p[0] 为基准点。


旋转卡壳什么的我就不贴代码了,不缺我这份。希望此日志能对陷在 POJ 2187 的同志们一些帮助……

此题卡我良久……同时感觉收获良多……特拿出来与大家分享。


--penhu, ShadowSword.

2012.8

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值