旋转卡壳

对踵点对定义:

如果过凸包上的两个点可以画一对平行直线,使凸包上的所有点都夹在两条平行线之间或落在平行线上,那么这两个点叫做一对对踵点。最远点对必然属于对踵点集(结论。可能是构成凸包最长的对角线一定是一对对踵点,不明。。。)

旋转卡壳寻找对踵点的基本思路是:

1.找到凸包最高点与最低点,作水平的一对平行线。规定一个时针方向两条线保持平行沿当前所在点转动。
2.当一条线与凸包的一条边重合时,记录重合边的另一个点与另一条线所在的点为一对对踵点。
3.重复第二步操作,直至重新回到最开始的一对对踵点

实现:

考虑一般情况是一条直线与一条边重合 ( i+1 -> i ),对于重合边靠右的一点i,根据对踵点的定义,一定在重边(i+1 -> i)与某一点 j 构成最大三角形是时有它的对踵点。记录i,j的距离,可以顺便记录点i+1与j的距离。j从0开始找,找到后再到下一条边i+1找与它对应的j,与他对应的j必然在当前j的位置或者它之后。这样就可以做到O(n)寻找,对踵点最多有3n/2对。

double rotat(Point ch[], int n)
{
    int j = 0;
    double ans = 0;
    ch[n] = ch[0];
    for(int i = 0; i < n; i++){
        while(cross(ch[i], ch[i+1], ch[j]) < cross(ch[i], ch[i+1], ch[j+1]))
            j = (j+1)%n;
        ans = max(ans, max(dis2(ch[i], ch[j]), dis2(ch[i+1], ch[j])));
    }
    return ans;
}

POJ2781

#include <stack>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100000 + 5;
const double PI = acos(-1.0);

struct Point{
    double x, y;
    bool extreme;
    int pos;
};
Point P[maxn];

int n;
Point TB[maxn];
int cntTB;

double dis(Point A, Point B)
{
    return sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y));
}

double dis2(Point A, Point B)
{
    return (A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y);
}

double cross(Point O, Point A, Point B)
{
    return (A.x - O.x)*(B.y - O.y) - (A.y - O.y)*(B.x - O.x);
}

bool cmp(Point A, Point B)
{
    int cur = 0;
    double temp = cross(P[cur], A, B);
    if(temp > 0) return true;
    else if(temp == 0 && (dis(P[cur], A) < dis(P[cur], B))) return true;
    else return false;
}

bool ToLeft(Point O, Point A, Point B)
{
    double temp = cross(O, A, B);
    if(temp > 0) return true;
    else if(temp == 0 && (dis(O, A) < dis(O, B))) return true;
    else return false;
}

void Graham()
{
    sort(P+1, P+n, cmp);  ///extreme angle sort

    stack<Point> s1, s2;
    while(!s1.empty()) s1.pop();
    while(!s2.empty()) s2.pop();

    if(n == 1){
        s1.push(P[0]);
    }
    else if(n == 2){
        s1.push(P[0]);
        s1.push(P[1]);
    }
    else{
        s1.push(P[0]), s1.push(P[1]);
        for(int i = n-1; i >=2; i--) s2.push(P[i]);

        while(!s2.empty()){
            Point B = s1.top();s1.pop();
            Point A = s1.top();
            Point C = s2.top();
            B.extreme = false;
            if(ToLeft(A, B, C)){
                s2.pop();
                s1.push(B), s1.push(C);
            }
        }
    }

    Point a[maxn];
    int cnta = 0;
    while(!s1.empty()){
        a[cnta++] = s1.top();
        s1.pop();
    }

    for(int i = cnta-1; i >= 0; i--)
        TB[cntTB++] = a[i];
}

double rotat(Point ch[], int n)
{
    int j = 0;
    double ans = 0;
    ch[n] = ch[0];
    for(int i = 0; i < n; i++){
        while(cross(ch[i], ch[i+1], ch[j]) < cross(ch[i], ch[i+1], ch[j+1]))
            j = (j+1)%n;
        ans = max(ans, max(dis2(ch[i], ch[j]), dis2(ch[i+1], ch[j])));
    }
    return ans;
}

int main()
{
    while(~scanf("%d", &n)){
        int tmp = 0;
        cntTB = 0;
        for(int i = 0; i < n; i++){
            scanf("%lf%lf", &P[i].x, &P[i].y);
            P[i].pos = i;
            P[i].extreme = false;
            if(P[i].y < P[tmp].y || (P[i].y == P[tmp].y && P[i].x < P[tmp].x))
                tmp = i;
        }
        swap(P[tmp], P[0]);
        Graham();
        printf("%.f\n", rotat(TB, cntTB));
    }
    return 0;
}
posted @ 2017-07-10 22:13  />.<\  阅读(219)  评论(0编辑  收藏  举报