poj 2187

来源:互联网 发布:黑社会2 知乎 编辑:程序博客网 时间:2024/05/28 01:35

题目概述

平面直角坐标系中给定N个相互独立的点的坐标x,y,求最远两点间距离的平方

时限

3000ms/9000ms

输入

第一行正整数N,其后N行,每行两个整数x,y,输入到EOF为止

限制

2<=N<=50000;-10000<=x,y<=10000

输出

每行一个数,为所求距离的平方

样例输入

4
0 0
0 1
1 1
1 0
8
5 15
15 10
0 0
10 0
-5 10
0 10
5 5
-1 6
2
0 0
0 1
4
0 0
0 1
0 2
0 3

样例输出

2
400
1
9

讨论

计算几何,旋转卡(qia3)壳(qiao4),当然也得求凸包,题目已经直接抽象好了,主要思想就是求旋转卡壳,要么理解思想,要么背模版,好在这部分的资料相当好找,额也就不赘述了
实现方面,基本无大坑,只是注意旋转卡壳的要可靠,最后需要用两个点比较取最远然后再和以前的最远比较,这点第二组样例可以测试,其次就是点数过少(样例三)和全部共线(样例四)的情况,不过由于andrew凸包的鲁棒性,这两条不单独考虑也不会出问题
然而额由于困成树懒,写旋转卡壳时忘了是在栈中,又样例数据凑巧,故连WA多次
题解代码测试了两种做法,andrew凸包+枚举=线性对数级+平方级,andrew凸包+旋转卡壳=线性对数级+线性级,不过由于向量积常数过大,前者仅测一次便明显快于后者,哪怕用内联,max用宏函数,也一样较慢(额交了近10次都比不上)

题解状态

枚举:564K,79MS,C++,1239B
旋转卡壳:564K,94MS,C++,1408B

题解代码

枚举

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;#define INF 0x3f3f3f3f#define MAXN 50003#define memset0(a) memset(a,0,sizeof(a))#define EPS 1e-8struct Pt//point 点的结构{    int x, y;    bool operator<(const Pt &b)const    {        return y != b.y ? y < b.y : x < b.x;    }}pts[MAXN];int N;//点总数int stk[MAXN], top;//stack 数组模拟栈及其栈顶int xp(Pt &a, Pt &b, Pt &c)//向量积{    return (a.x - b.x)*(c.y - b.y) - (a.y - b.y)*(c.x - b.x);}int dis2(Pt &a, Pt &b)//两点间距离的平方{    return (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);}int fun(){    for (int p = 0; p < N; p++)        scanf("%d%d", &pts[p].x, &pts[p].y);//input    sort(pts, pts + N);//排序 下面是andrew凸包 还没忘怎么写    for (int p = 0; p < N; p++) {        while (top > 1 && xp(pts[p], pts[stk[top - 2]], pts[stk[top - 1]]) >= 0)            top--;        stk[top++] = p;    }    int stklen = top;    for (int p = N - 2; p >= 0; p--) {        while (top > stklen&&xp(pts[p], pts[stk[top - 2]], pts[stk[top - 1]]) >= 0)            top--;        stk[top++] = p;    }    top--;//去除多算了一次的凸包起点    int most = -INF;//下面枚举找出最远点 只有两处常数    for (int p = 0; p < top; p++)        for (int i = p + 1; i < top; i++)            most = max(most, dis2(pts[stk[p]], pts[stk[i]]));    return most;}int main(void){    //freopen("vs_cin.txt", "r", stdin);    //freopen("vs_cout.txt", "w", stdout);    while (~scanf("%d", &N)) {//input        printf("%d\n", fun());//output        top = 0;//清零栈    }}

旋转卡壳:

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;#define INF 0x3f3f3f3f#define MAXN 50003#define memset0(a) memset(a,0,sizeof(a))#define EPS 1e-8struct Pt//point 点的结构{    int x, y;    bool operator<(const Pt &b)const    {        return y != b.y ? y < b.y : x < b.x;    }}pts[MAXN];int N;//点总数int stk[MAXN], top;//stack 数组模拟栈及其栈顶inline int xp(Pt &a, Pt &b, Pt &c)//向量积 内联的{    return (a.x - b.x)*(c.y - b.y) - (a.y - b.y)*(c.x - b.x);}inline int dis2(Pt &a, Pt &b)//两点间距离的平方 也是内联{    return (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);}int fun(){    for (int p = 0; p < N; p++)        scanf("%d%d", &pts[p].x, &pts[p].y);//input    sort(pts, pts + N);//排序 下面是andrew凸包    for (int p = 0; p < N; p++) {        while (top > 1 && xp(pts[p], pts[stk[top - 2]], pts[stk[top - 1]]) >= 0)            top--;        stk[top++] = p;    }    int stklen = top;    for (int p = N - 2; p >= 0; p--) {        while (top > stklen&&xp(pts[p], pts[stk[top - 2]], pts[stk[top - 1]]) >= 0)            top--;        stk[top++] = p;    }    top--;//去除算了两次的凸包起点 下面是旋转卡壳    int most = 0, app = 2;//most 最远两点距离平方 antipodal_point 对踵点 肯定不会是前两个点    for (int p = 0; p < top; p++) {//枚举栈中的点 是栈 不是所有点        while (xp(pts[stk[p + 1]], pts[stk[p]], pts[stk[app + 1]])>xp(pts[stk[p + 1]], pts[stk[p]], pts[stk[app]]))//对于枚举的点 其对踵点是其余点中到枚举的点距离最远的点 由于凸包是逆时针构成 两点距离到极大值时便成对踵点 这里不直接求距离 而是利用三角形(有向)面积比较            app = (app + 1) % top;        most = max(most, max(dis2(pts[stk[p]], pts[stk[app]]), dis2(pts[stk[p + 1]], pts[stk[app]])));//由于每次实际上是枚举凸包上相邻两个点所成的线 因而需要判断到底线上哪个点更远一点    }    return most;}int main(void){    //freopen("vs_cin.txt", "r", stdin);    //freopen("vs_cout.txt", "w", stdout);    while (~scanf("%d", &N)) {//input        printf("%d\n", fun());//output        top = 0;//清零栈    }}

EOF

0 0
原创粉丝点击