Graham Scan算法解决凸包问题 学习笔记

Graham Scan解决凸包

1.凸包

百度百科
凸包(Convex Hull)是一个计算几何(图形学)中的概念。
在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1,...Xn)的凸组合来构造.
在二维欧几里得空间中,凸包可想象为一条刚好包著所有点的橡皮圈。
用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。

形象化说明:凸多边形

2.Graham扫描法

2.1思想:

2.1.1首先找到整个点集中位置最低的一个点P(y轴数值最小的),同时这个点也必然是凸包的一个顶点。还有最高的点。

2.1.2从P开始朝其它各个点作射线开始扫描,依次考察(用叉积确定极角递增的顺序扫描)

2.2步骤:

2.2.1先找到最左下角的点

2.2.2剩余点按照极角排序,角度相同的则按照距离远近排序

2.2.3压栈检查

 

2.3伪代码

void graham(int n){
  令P0为Q中X-Y坐标下y坐标排序最小的点; m=n-1;
  按前面的②取好序列P1,P2,P3,…,Pm;
  设定一个栈S;依次压P0, P1, P2进栈S;栈顶位置top=2;
  for ( i=3; i<=m;i++){
   while (次栈顶元素Ptop-1、栈顶元素Ptop
                             和Pi在Ptop所形成的角不是向左转)
     将Ptop出栈;
     压Pi进栈S;
   } 
   输出栈S中元素;
}

 

题目:HDU 1348 http://acm.hdu.edu.cn/showproblem.php?pid=1348

题目大意:求一个圆 + 一个凸包的周长之和

AC代码:(参考卿学姐)

#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 1005;
const double PI = 3.1415926;
struct node{
    double x,y;
}p[maxn],P[maxn];                                    //p为点集,P为凸包点集
int n,tot;                                           //tot用于模拟栈
double ans,l;
double X(node A,node B,node C) {                     //定义叉乘
    return (B.x - A.x) * (C.y - A.y) - (C.x - A.x) * (B.y - A.y);
}
double len(node A,node B) {                          //计算距离
    return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}
bool cmp(node A,node B) {                            //排序,以距离最左下角点角度为第一要素,角度相同的情况下距离为第二要素
    double pp = X(p[0],A,B);
    if(pp > 0) return true;
    if(pp < 0) return false;
    return len(p[0],A) < len(p[0],B);
}
int main() {
    int t;
    cin >> t;
    for(int i = 1; i <= t; i++) {
        if(i != 1) cout << endl;                
        cin >> n >> l;
        ans = 2 * PI * l;
        for(int i = 0; i < n; i++) {
            cin >> p[i].x >> p[i].y;
        }
        if(n == 1) cout << ans << endl;
        else if (n == 2) cout << ans + len(p[0],p[1]) << endl;
        else {                                        //找到最左下角的点
            for(int i =0;i <n ;i++) {
                if(p[i].y<p[0].y) {
                    swap(p[i],p[0]);
                }else if(p[i].y == p[0].y && p[i].x < p[0].x) {
                    swap(p[i],p[0]);
                }
            }
            sort(p+1,p+n,cmp);
            P[0] = p[0];
            P[1] = p[1];
            tot = 1;
            for(int i = 2 ;i < n;i++) {               //开始扫描求凸包
                while(tot > 0 && X(P[tot - 1],P[tot],p[i])<=0) tot--;
                tot++;
                P[tot] = p[i];
            }
            for(int i =0;i<tot;i++) {
                ans += len(P[i],P[i+1]);
            }
            ans += len(P[0],P[tot]);
            printf("%.0lf\n",ans);
        }
    }

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值