POJ 2187 计算几何之旋转卡壳

首先这个题目可以用先求凸包再暴力枚举的方法通过,原因不是数据出水了,而是因为有一个奇葩的结论就是坐标值不超过K的不同的多边形最多不会超过 sqrt (K) 个 所以求出凸包后暴力枚举时间复杂度是可以接受的

  1. 关于多边形的切线 : 就是说一条直线与多边形相交,并且多边形在直线的一侧,这样的直线称之为多边形的切线,
  2. 关于多边形的对踵点 : 多边形的对踵点就是 : 两条平行的多边形切线上的与多边形相交的点。 对踵点有三种情况

    1. 两条切线都只与多边形有一个交点
    2. 两条切线都与多边形有无数多个交点
    3. 一个有一个交点,另一个有无穷多个交点
  3. 关于旋转卡壳 : 是一种求平面上所有点的最远对点的算法,我们知道可以通过求凸包 + 枚举凸包上的点的方式求出平面上所有点的最远距离,但是这种方法在凸包上的点过多的时候时间复杂度退化到 O (n ^ 2) 对于有些题目是不可以接受的,这个时候就要采取旋转卡壳的方法求平面上任意两个点的最远距离。(也叫做多边形的直径) 因为可以保证对踵点的个数不会超过 3/2n 个 所以算法的时间复杂度可以保证

  4. 具体实现
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 50005;
const double eps = 1e-8;
const int INF = 1e9 + 7;
struct point {
    int x,y;
}p[maxn];
point poly[maxn] = {0};
point init;
int n;
int tot;

int ab (int x) {return x > 0 ? x : -x;}

int dist (point a,point b) {
    return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}

int cross (int x1,int y1,int x2,int y2) {
    return x1 * y2 - x2 * y1;
}

bool cmp (const point a,const point b) {
    if (cross(a.x - init.x,a.y - init.y,b.x - init.x,b.y - init.y) != 0) {
        return cross(a.x - init.x,a.y - init.y,b.x - init.x,b.y - init.y) > 0;
    }
    else {
        return dist (a,init) < dist (b,init);
    }
}

void tubao () {
    for (int i = 1;i <= 2; ++ i) poly[i] = p[i];
    tot = 2;
    for (int i = 3;i <= n; ++ i) {
        while (tot >= 2) {
            point p1,p2,p3;
            p1 = poly[tot];
            p2 = poly[tot - 1];
            p3 = p[i];
            if (cross (p1.x - p2.x,p1.y - p2.y,p3.x - p1.x,p3.y - p1.y) <= 0) tot --;
            else break;
        }
        poly[++ tot] = p[i];
    }
}

int rotating () {
    if (tot == 2) return dist (poly[1],poly[2]);
    poly[++tot] = poly[1];
    int ans = 0;
    int u = 2;
    for (int i = 1;i < tot; ++ i) {
        int mxd = max (dist (poly[u],poly[i]),dist (p[u],poly[i + 1]));
        ans = max (ans,mxd);
        while (ab (cross(poly[i].x - poly[u].x, poly[i].y - poly[u].y, poly[i + 1].x - poly[u].x, poly[i + 1].y - poly[u].y)) <= ab (cross(poly[i].x - poly[u + 1].x, poly[i].y - poly[u + 1].y, poly[i + 1].x - poly[u + 1].x, poly[i + 1].y - poly[u + 1].y))) {
                u = u % tot + 1;
                mxd = max (dist (poly[u],poly[i]),dist (p[u],poly[i + 1]));
                ans = max (ans,mxd);
        }
    }
    return ans;
}

int main () {
    ios_base :: sync_with_stdio(false);
    while (cin >> n) {
        for (int i = 1;i <= n; ++ i) {
            cin >> p[i].x >> p[i].y;
            if (p[i].y < p[1].y) {
                swap (p[1],p[i]);
            }
            else if (p[i].y == p[1].y && p[i].x < p[1].x) {
                swap (p[1],p[i]);
            }
        }
        if (n == 2) {
            cout << dist (p[1],p[2]) << endl;
            continue;
        }
        init = p[1];
        sort (p + 1,p + n + 1,cmp);
        tubao();
        cout << rotating() << endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值