poj 2187 Beauty Contest 经典题目:凸包+旋转卡壳

题目:http://poj.org/problem?id=2187

/*经典题目:凸包加旋转卡壳
凸包第一题,由于本人的粗心,T了很久,最终发现是旋转卡壳部分写错了,不过学会了旋转卡壳的两种姿势
先求凸包,然后旋转卡壳求出凸包直径即可
*/
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

const int N = 50010;

int n;
struct P
{
    int x, y;
    P(){}
    P(int x, int y) : x(x), y(y) {}
    P operator- (P p)
    {
        return P(x - p.x, y - p.y);
    }
    int dot(P p) //向量点乘
    {
        return x * p.x + y * p.y;
    }
    int det(P p) //向量叉乘
    {
        return x * p.y - p.x * y;
    }
    int len(P p)
    {
        return (x - p.x) * (x - p.x) + (y - p.y) * (y - p.y);
    }
}ps[N], qs[N];

bool cmp(P a, P b)
{
    if(a.x == b.x) return a.y < b.y;
    else return a.x < b.x;
}

int graham() //求凸包
{
    int k = 0;
    for(int i = 0; i < n; i++) //求凸包下半侧
    {
        while(k > 1 && (qs[k-1] - qs[k-2]).det(ps[i] - qs[k-1]) <= 0) k--;
        qs[k++] = ps[i];
    }
    for(int i = n - 2, t = k; i >= 0; i--) //求凸包上半侧
    {
        while(k > t && (qs[k-1] - qs[k-2]).det(ps[i] - qs[k-1]) <= 0) k--;
        qs[k++] = ps[i];
    }

    return k - 1;
}

int rotating_calipers(int m) //旋转卡壳
{
    if(m == 2) //此种方法必须特判m为2时,不然会T
        return qs[0].len(qs[1]);

    int i = 0, j = 0;
    for(int k = 0; k < m; k++) //找出最左点和最有点,即x轴方向上的对蹱点
    {
        if(! cmp(qs[i], qs[k])) i = k; //最左点
        if(cmp(qs[j], qs[k])) j = k; //最右点
    }

    int si = i, sj = j, res = 0;
    while(i != sj || j != si) //将方向逐步旋转180度
    {
        res = max(res, qs[i].len(qs[j]));
        /*判断先转到边i-(i+1)的法线方向还是j-(j+1)的法线方向*/
        if((qs[(i+1)%m] - qs[i]).det(qs[(j+1)%m] - qs[j]) < 0) i = (i + 1) % m; //先转到边i-(i+1)的法线方向
        else j = (j + 1) % m; //先转到边j-(j+1)的法线方向
    }

    return res;
}

/*
int rotating_calipers(int m) //旋转卡壳
{
    int j = 1, res = 0;
    qs[m] = qs[0];
    for(int i = 0; i < m; i++) //依次用叉积找出凸包每一条边对应的最高点
    {
        //同底不同高,每次用面积判断高的大小就可以了
        while((qs[i+1] - qs[i]).det(qs[j+1] - qs[i]) > (qs[i+1] - qs[i]).det(qs[j] - qs[i]))
            j = (j + 1) % m;
        res = max(res, max(qs[j].len(qs[i]), qs[j+1].len(qs[i+1]))); //每条底上有两个点,所以要 max 两次
    }

    return res;
}
*/

int main()
{

    while(~ scanf("%d", &n))
    {
        for(int i = 0; i < n; i++)
            scanf("%d%d", &ps[i].x, &ps[i].y);

        sort(ps, ps + n, cmp);
        int m = graham();
        printf("%d\n", rotating_calipers(m));
    }

    return 0;
}




你可能感兴趣的:(计算几何)