10.1 叉积 ,极角排序,扫描法求凸包

凸包:用一个凸多边形将所有点围起来,这个凸多边形就是凸包

1.先要引入一个数学工具,向量叉积

  |c|=|a×b|=|a| |b|sinα   (α为a,b向量之间的夹角)

则 |c| 为向量a ,b所组成的平行四边形的面积

这里是用叉积判断两向量的相对位置关系(非常有用!)

则 a x b < 0 (a在b的逆时针方向 ) , b x a > 0(b在a的顺时针方向)

//求叉积
struct node{
    double x ,y;
    node operator -( const node & s ){
        return {x-s.x , y-s.y};
    }
}p[N] ,s[N];

inline double X( node a ,node b ){
    return a.x*b.y - b.x*a.y;
}

2. Graham扫描法求凸包

1)找出所有点中在最左下角的点定为极点

    //找左下边界点
     int k = 1;
     rep( i ,2 ,n ){
         if( p[i].y < p[k].y || p[i].y == p[k].y && p[i].x < p[k].x )
            k = i;
     }
     swap( p[1] ,p[k] );

2)利用叉积进行极角排序

//极角比较
bool cmp (  node &a , node &b ){
        double x = X(a-p[1] ,b-p[1]);
        //叉积判断向量位置关系
        if( x > 0 )return 1;
        if( x==0 && dis( a ,p[1])<dis( b ,p[1]) )return 1;
        return 0;
}
     //极角排序
     sort( p+2 ,p+n+1 ,cmp);

3)存凸包

s为存凸包的栈 ,t为栈顶

则由以下关系用叉积取舍s中的点

     //将凸包存在s中
     s[1] = p[1];
     s[2] = p[2];
     int t = 2;
     rep( i ,3 ,n ){
         while( t >= 2 && mul( s[t-1] ,s[t] ,p[i] )<=0 )t--;
         s[++t] = p[i];
     }

然后凸包就求出来了

模板题 :

P2742 【模板】二维凸包 / [USACO5.1]圈奶牛

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <cstring>
#include <vector>
#include <map>
#include <unordered_map>
#define mem( a ,x ) memset( a , x ,sizeof(a) )
#define rep( i ,x ,y ) for( int i = x ; i<=y ;i++ )
#define lson l ,mid ,pos<<1
#define rson mid+1 ,r ,pos<<1|1
#define Fi first
#define Se second

using namespace std;
typedef long long ll ;
typedef pair<int ,int> pii;
typedef pair<ll ,int> pli;
const ll inf = 0x3f3f3f3f;
const int N = 1e5+5;
const ll mod = 1e9+7;

int n ,m;
//求叉积
struct node{
    double x ,y;
    node operator -( const node & s ){
        return {x-s.x , y-s.y};
    }
}p[N] ,s[N];

inline double X( node a ,node b ){
    return a.x*b.y - b.x*a.y;
}

inline double dis( node a ,node b ){
    return sqrt( (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

inline double mul( node a ,node b ,node c ){
    return X(b-a ,c-a);
}
//极角比较
bool cmp (  node &a , node &b ){
        double x = X(a-p[1] ,b-p[1]);
        //叉积判断向量位置关系
        if( x > 0 )return 1;
        if( x==0 && dis( a ,p[1])<dis( b ,p[1]) )return 1;
        return 0;
}

//Graham 扫描

int graham( ){
    //找左下边界点
     int k = 1;
     rep( i ,2 ,n ){
         if( p[i].y < p[k].y || p[i].y == p[k].y && p[i].x < p[k].x )
            k = i;
     }
     swap( p[1] ,p[k] );
     //极角排序
     sort( p+2 ,p+n+1 ,cmp);
     //将凸包存在s中
     s[1] = p[1];
     s[2] = p[2];
     int t = 2;
     rep( i ,3 ,n ){
         while( t >= 2 && mul( s[t-1] ,s[t] ,p[i] )<=0 )t--;
         s[++t] = p[i];
     }

     return t;
}

int main( ){
    scanf("%d" ,&n);
    rep( i ,1 ,n ){
        scanf("%lf%lf" ,&p[i].x ,&p[i].y );
    }
    int sz = graham( );

    double ans = dis(s[1] ,s[sz]);
    rep( i ,1 ,sz-1 )ans += dis( s[i] ,s[i+1] ) ;

    printf("%.2f" ,ans);
    return 0;
}

原文地址:https://www.cnblogs.com/-ifrush/p/11615398.html

时间: 2024-10-17 05:59:28

10.1 叉积 ,极角排序,扫描法求凸包的相关文章

(模板)poj1113(graham扫描法求凸包)

题目链接:https://vjudge.net/problem/POJ-1113 题意:简化下题意即求凸包的周长+2×PI×r. 思路:用graham求凸包,模板是kuangbin的. AC code: #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int maxn=1005; const double PI=

poj 1106 Transmitters (计算几何,叉积||极角排序)

Transmitters Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 4817   Accepted: 2576 Description In a wireless network with multiple transmitters sending on the same frequencies, it is often a requirement that signals don't overlap, or at

极角排序+求锐角三角形个数——uva12123

这个版本还不能处理三点共线的情况(处理起来其实比较麻烦) 可以用atan2来排序(较为简单,但是精度误差大),也可以用叉积排序(比较优秀) atan2 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <cstdlib> using namespace std; typedef

UVA 1606 Amphiphilic Carbon Molecules 两亲性分子 (极角排序或叉积,扫描法)

任意线可以贪心移动到两点上.直接枚举O(n^3),会TLE. 所以采取扫描法,选基准点,然后根据极角或者两两做叉积比较进行排排序,然后扫一遍就好了.旋转的时候在O(1)时间推出下一种情况,总复杂度为O(n^2logN)就可以过了. 另外,本题有个很巧妙的技巧,就是一点等效与相反坐标的相反颜色的点. 第一次写,细节还是蛮多的,花了好久才搞清所有细节... 极角排序版,比较容易理解,932ms. #include<bits/stdc++.h> using namespace std; const

Wall--POJ1113(极角排序+求凸包)

http://poj.org/problem?id=1113 题目大意:现在要给n个点,让你修一个围墙把这些点围起来,距离最小是l 分析  :现在就是求凸包的周长然后再加上一个圆的周长 #include<stdio.h> #include<string.h> #include<stdlib.h> #include<math.h> #include<algorithm> #include<iostream> #include<qu

Gym 101986D Making Perimeter of the Convex Hull Shortest(凸包+极角排序)

首先肯定是构造一个完整的凸包包括所有的点,那么要使得刚好有两个点在外面,满足这个条件的只有三种情况. 1.两个在凸包上但是不连续的两个点. 2.两个在凸包上但是连续的两个点. 3.一个在凸包上,还有一个在这个点去掉后这段新凸包边上的一个点. 如何快速的截取新凸包的点是谁呢,我们可以将整个凸包划分区域,每个点删掉后,只可能在这块区域内选择新的点.那么我们就可以随机在凸包内部选择一个点,我使用的是凸包的重心作为坐标原点o,那么整个凸包移到原点处,然后在这个点的左侧和右侧的三角形区域内才是有可能构成新

【计算几何】【凸包】【极角排序】【二分】Gym - 101128J - Saint John Festival

平面上n个红点,m个黑点,问你多少个黑点至少在一个红三角形内. 对红点求凸包后,转化为询问有多少个黑点在凸包内. 点在凸多边形内部判定,选定一个凸包上的点作原点,对凸包三角剖分,将其他的点极角排序之后,使用二分法就可以判定点在哪个剖分出来的三角形的夹角内,然后用叉积即可判定其在凸包内还是外,O(logn): http://www.cnblogs.com/dream-wind/archive/2012/05/23/2514694.html #include<cstdio> #include<

使用Graham扫描法求二维凸包的一个程序

1 #include "includeall.h" 2 #include "Link.class.h" 3 4 int RightOrLeft(float x1,float y1,float x2,float y2,float x3,float y3)//判断第三个点在前两个点连成的直线的哪个位置,-1 左边,0,直线上,1 右边 5 { 6 float X=(y3-y1)*(x2-x1)/(y2-y1)+x1; 7 if(X<x3) 8 { 9 return

poj1113Wall 求凸包周长 Graham扫描法

#include<iostream> #include<algorithm> #include<cmath> using namespace std; typedef pair<int ,int > ll; ll num,dot[1010]; int i; const double pi=3.1415926535898; ll operator -(ll a,ll b) { return make_pair(a.first-b.first,a.second-