【算法学习笔记】36.凸包 求最大两点距离 SJTU OJ 1244 Date A Live

Description

某助教有好多好多妹纸,其中不乏来自五道口与东川路等男子职业技术学校的。然而,遥远的距离让他不得不花费大量的时间奔波于众多城市之间。为了更好地安排自己的约会计划,他想知道最远的两只妹纸之间的距离是多少。

Input Format

第一行有一个整数n,表示妹纸的数量。

接下来n行,每行两个实数x,y,表示妹纸的坐标(假定在一个平面直角坐标系上)。

对于80%的数据,n<=2000

对于90%的数据,n<=10000

对于100%的数据,n<=100000

Output Format

输出一个实数,表示最远的妹纸间的欧几里得距离(即直线距离)。答案保留4位小数。

Sample Input

3
1 1
0 0
10 1

Sample Output

10.0499

数学模型就是求一个点集最远两个点的距离大小.

想法是先用Graham扫描法来建立凸包集合.然后有两条路,其中一个就是Cn2枚举出任意两个点 求出距离另一个是用传说中的旋转卡壳法来求.复杂度为On ,基本思想就是旋转寻找和凸包之边的最远的点,由于凸包的凸性质,导致这个最远点的旋转方向和边的旋转方向一致,所以压缩至On

以下代码是用数组实现的凸包+Cn2方法,数组实现比STL的vector要快很多,而且也比较容易看懂.几个值得注意的地方是1.基点的选择, 选择最左下的点,还是选最低点里偏左的2.比较的方法, 余弦定理还是外积判断?3.栈顶指针的竖直和栈长度的关系...= =

#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio> 

using namespace std;
struct Point
{
    double x, y;
};

Point pointSet[100005];
int convexHull[100005];//凸包只要存储点的ID即可
int ps_len=0;//点集的大小
int ch_len=0;//凸包的栈指针

ostream& operator << (ostream &out, const Point& p) {
    out <<‘(‘<<p.x<<‘,‘<<p.y<<‘)‘<<endl ;
    return out;
}

//计算外积 负表示顺时针 正表示逆时针 0表示共线
inline double crossProduct(const Point &p1, const Point &p2,const Point &p0){
    return (p1.x-p0.x)*(p2.y-p0.y) - (p1.y-p0.y)*(p2.x-p0.x);
}

//返回正数表示要把两个数进行交换 也就是把b放在a前面 逆时针排序
int cmpTwoPoints(const void *a, const void *b){
    Point* p1 = (Point*) a;
    Point* p2 = (Point*) b;
    //注意:此时基点已经是pointSet[0]
    //第一种方法利用余弦定理来进行判断
    //第二种方法利用叉乘来表示 如果 < p0,p1 , p0,p2 >是负数 则表示指向纸面内部 也就是顺时针
    double res = crossProduct(*p1, *p2, pointSet[0]);
    if(res<0)
        return 1;
    if(res == 0 and p2->x < p1->x)//如果共线且p2更近
        return 1;
    return -1;
}

// inline Point CH(int CHid)
// {
//     return pointSet[convexHull[CHid]];
// }

//建立凸包
void BuildConvexHull(){
    //Step1 找到基准点 最下偏左的点
    int basePoint=0;
    for (int i = 1; i < ps_len; ++i)
    {
        //if(    pointSet[i].y < pointSet[basePoint].y or (pointSet[i].y==pointSet[basePoint].y and pointSet[i].x<pointSet[basePoint].x) )
        if(    pointSet[i].x < pointSet[basePoint].x or (pointSet[i].x==pointSet[basePoint].x and pointSet[i].y<pointSet[basePoint].y) )
            basePoint = i;
    }
    swap(pointSet[basePoint],pointSet[0]);//把基点换到ps的第一个位置

    //Step2 把pointSet里的点按照他们与基点的连线与x轴的夹角的大小进行排序
    qsort(pointSet+1,ps_len-1,sizeof(Point),cmpTwoPoints);//不算基点 进行排序
   // PrintPS();
    //Step3 开始build凸包
    convexHull[0] = 0;//基点肯定是凸包上的第一个点 入栈
    convexHull[1] = 1;//预设第一个非基点的点也是凸包上的点  入栈
    convexHull[2] = 2;
    ch_len = 2;//指针指向栈顶元素
    //开始DFS --> 栈

    for (int i = 3; i < ps_len ; ++i)//研究剩余的所有点
    {
        //假如让它入栈 它可以和 前一个点 前前一个点 是逆时针旋转的 那么就暂时可以加入 否则 踢出上x个点 直到可以继续
        while(ch_len > 0 and crossProduct(pointSet[convexHull[ch_len]],pointSet[i],pointSet[convexHull[ch_len-1]]) < 0)
            ch_len--;//抛弃这个凹点
        convexHull[++ch_len] = i;//暂时让此点入栈
    }
}

inline double _dis(Point p1, Point p2){
    return sqrt(pow((p1.x-p2.x),2)+pow((p1.y-p2.y),2));
}
inline double _max(double a,double b){
    return a>b ? a : b;
}

double Cal(){
    double res = 0;
    for (int i = 0; i < ch_len+1-1; ++i)
        for (int j = i+1; j < ch_len+1 ; ++j)
            res = _max(res,_dis(pointSet[convexHull[i]],pointSet[convexHull[j]]));
    return res;
}
int main(int argc, char const *argv[])
{
    cin>>ps_len;
    for (int i = 0; i < ps_len; ++i)
        scanf("%lf %lf",&pointSet[i].x,&pointSet[i].y);
    BuildConvexHull();
    printf("%.4lf",Cal());
    return 0;
}

几个相关博客:

http://www.cnblogs.com/devymex/archive/2010/08/09/1795392.html

http://blog.csdn.net/hackbuteer1/article/details/7484746

				
时间: 2024-10-06 00:31:02

【算法学习笔记】36.凸包 求最大两点距离 SJTU OJ 1244 Date A Live的相关文章

【算法学习笔记】40.树状数组 动态规划 SJTU OJ 1289 扑克牌分组

Description cxt的扑克牌越来越先进了,这回牌面的点数还可以是负数, 这回cxt准备给扑克牌分组,他打算将所有的牌分成若干个堆,每堆的牌面总和和都要大于零.由于扑克牌是按顺序排列的,所以一堆牌在原牌堆里面必须是连续的.请帮助cxt计算一下,存在多少种不同的分牌的方案.由于答案可能很大,只要输出答案除以1,000,000,009的余数即可. Input Format 第一行:单个整数:N,1 ≤ N ≤ 10^6 第二行到N + 1行:在第i + 1行有一个整数:Ai, 表示第i张牌牌

【算法学习笔记】42.正反DP 填充连续 SJTU OJ 1285 时晴时雨

1285. 时晴时雨 Description Taring 喜欢晴天,也喜欢雨天. Taring说:我想体验连续的K天的晴朗,去远足,去放歌:我还想再这K个晴天之后,再去体验连续的K天的云雨,去感受落雨时的轻语.这是令Taring最开心的事情了.其它的时间,Taring会在机房默默的编写着代码. 当然,Taring不想在这连续的K个晴天和连续的K个雨天里被机房的事务打扰或者被自然天气的变化中断.也就是说,这K个晴天和K个雨天必须是连续的,但是他们之间并不需要时间连续.显然的,Taring如果在感

【算法学习笔记】35.高精度 竖式乘法 SJTU OJ 1274

Description 输入a,b 输出a*b的竖式乘法,格式见样例. Sample Input1 11 9 Sample Output1 11 9 -- 99 Sample Input2 10 10 Sample Output2 10 10 --- 100 Sample Input3 101 101 Sample Output3 101 101 ----- 101 101 ----- 10201 Sample Input4 10086 2 Sample Output4 2 10086 ----

【算法学习笔记】55.DFS 记忆化搜索 SJTU OJ 1063 小M爱滑雪

Description 小M超级喜欢滑雪~~ 滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当小M滑到坡底,便不得不再次走上坡或者等待升降机来载你.小M想知道滑雪场中最长底的滑坡.滑雪场由一个二维数组给出.数组的每个数字代表点距离水平面的相对距离.下面是一个例子 1 2 3 4 5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 10 9 小M可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小.在上面的例子中,一条可滑行的

【算法学习笔记】39.字符串处理 单词分割 SJTU OJ 1302 缩进格式

1302. 缩进格式 Description 小z想和小y愉快的玩耍,但是小y在写程序.程序写好了,但是小y怎么调试也过不了.小z帮小y看了一下就不想看了,因为小y虽然是萌妹子,但是她的程序缩进实在是不忍直视.于是小z决定帮她纠正. 程序里的每一行语句分为单词和空格,ASCII码从33到126的一段连续字符串是单词,而单词之间由ASCII码为32的空格分开.小z的缩进方法具体来说是这样:对于每一行的第 i 个单词,它的第一个字符的位置不能小于其它每一行的第 1 至第 i−1 个单词,且每个单词的

【算法学习笔记】69. 枚举法 字典序处理 SJTU OJ 1047 The Clocks

我们发现这个题里每一种“移动套餐”用的次数只有0,1,2,3 是有效的,4和0是一样的. 所以我们开一个数组rot[10]来记录这9个套餐分别用了多少次. 字典序的处理和我们的枚举顺序息息相关. 我们从 000000000 到 333333333 来枚举的话,第一个符合条件的结果当然就是所有答案中字典序最小的一个了. 枚举这个排列我们需要用9个for,当然也可以用递归来实现,不过我还是喜欢视觉感比较强烈的9个for.... 在每一种排列下,我们需要通过这些套餐的方案来计算临时结果path. 然后

【算法学习笔记】44. 并查集补充 SJTU OJ 3015 露子的星空

[题目描述] 已经深夜了,露子仍然在公园里仰望星空.你走近后,她对你说:“呜—,你看夜空中的星星.它们本来都是孤独地毫无联系,但人们赋予了它们各种地联想,在它们之间连上了线,便形成了夜空中无数的星座.”你回答:“是啊.为什么我们不自己创造一个美丽的星空呢?” 假设夜空中一共有n颗星星,它们初始时都没有连线.接下来,露子会给你m条指令.一共有以下三种指令: 1.在某两颗星星间连线.(可能会重复连接两颗星星) 2.询问你当前夜空中一共有多少个星座. 3.某两颗星星当前是否属于同一个星座. 其中星座是

算法学习笔记 最短路

图论中一个经典问题就是求最短路,最为基础和最为经典的算法莫过于 Dijkstra 和 Floyd 算法,一个是贪心算法,一个是动态规划,这也是算法中的两大经典代表.用一个简单图在纸上一步一步演算,也是很好理解的,理解透自己多默写几次即可记住,机试时主要的工作往往就是快速构造邻接矩阵了. 对于平时的练习,一个很厉害的 ACMer  @BenLin_BLY 说:"刷水题可以加快我们编程的速度,做经典则可以让我们触类旁通,初期如果遇见很多编不出,不妨就写伪代码,理思路,在纸上进行整体分析和一步步的演算

算法学习笔记 递归之 快速幂、斐波那契矩阵加速

递归的定义 原文地址为:http://blog.csdn.net/thisinnocence 递归和迭代是编程中最为常用的基本技巧,而且递归常常比迭代更为简洁和强大.它的定义就是:直接或间接调用自身.经典问题有:幂运算.阶乘.组合数.斐波那契数列.汉诺塔等.其算法思想: 原问题可分解子问题(必要条件): 原与分解后的子问题相似(递归方程): 分解次数有限(子问题有穷): 最终问题可直接解决(递归边界): 对于递归的应用与优化,直接递归时要预估时空复杂度,以免出现用时过长或者栈溢出.优化递归就是以