计算几何模板

来源:互联网 发布:c语言中的echo 编辑:程序博客网 时间:2024/06/05 15:45



求锐角三角形个数.

统计锐角的个数,设为 A,钝角和直角的个数,设为 B 
每个锐角三角形有三个锐角,每个钝角和直角三角形均贡献两个锐角 
所以答案即为 A2B3 
然后题解在对每个点极角排序之后,采用 two pointers的方式来找上下界 
比二分好写多了,也很方便,时间复杂度 O(N2logN)

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 2e3+10;int n;struct Point{    ll x,y;    Point(ll xx=0,ll yy=0)    {        x=xx;        y=yy;    }    Point operator -(const Point & a) const    {        return Point(x-a.x,y-a.y);    }}p[maxn];ll xmul(const Point &a,const Point &b){    return a.x*b.y - a.y*b.x;}ll dot(const Point &a,const Point &b){    return a.x*b.x+a.y*b.y;}bool cmp(const Point &a,const Point &b){    if(a.y * b.y <=0)    {        if(a.y >0 || b.y > 0) return a.y < b.y;        if(a.y == 0 && b.y ==0) return a.x < b.x;    }    return xmul(a,b) > 0;}vector<Point>vec;int main(){    while(~scanf("%d",&n))    {        ll zhidun = 0,rui = 0,ans = 0;        for(int i = 0;i < n;i++)            scanf("%lld %lld",&p[i].x,&p[i].y);        for(int cor = 0;cor < n;cor++)        {            vec.clear();            for(int i = 0;i < n ;i++)            {                if(i != cor)                {                    vec.emplace_back(p[i]-p[cor]);                }            }            sort(vec.begin(),vec.end(),cmp);            vec.insert(vec.end(),vec.begin(),vec.end());            int j=0,k=0,r=0;            for(int i=0;i<n-1;i++)            {                while(j<i+n-1&&xmul(vec[i],vec[j])==0&&dot(vec[i],vec[j])>0)                j++;                k=max(k,j);                while(k<i+n-1&&xmul(vec[i],vec[k])>0&&dot(vec[i],vec[k])>0)                k++;                r=max(r,k);                while(r<i+n-1&&xmul(vec[i],vec[r])>0)                r++;                rui += k-j;                zhidun+=r-k;            }        }        ans = (rui-2*zhidun)/3;        printf("%lld\n",ans);    }    return 0;}



射线法判断点和多边形位置关系

/*射线法:判断一个点是在多边形内部,边上还是在外部,时间复杂度为O(n);射线法可以正确用于凹多边形;射线法是使用最广泛的算法,这是由于相比较其他算法而言,它不但可以正确使用在凹多边形上,而且不需要考虑精度误差问题。该算法思想是从点出发向右水平做一条射线,计算该射线与多边形的边的相交点个数,当点不在多边形边上时,如果是奇数,那么点就一定在多边形内部,否则,在外部。需要注意的是: 射线法判断点和多边形位置关系,多边形的点要求必须是逆时针描述. */#include <stdio.h>#include <algorithm>#include <cstring>#include <cmath>using namespace std;const int N = 2010;const double eps = 1e-10;const int INF = 0x3f3f3f3f;struct point{    double x, y;    point(double x=0, double y=0) : x(x), y(y){}    friend point operator - (const point& p1, const point& p2)    {        return point(p1.x-p2.x, p1.y-p2.y);    }    friend double operator ^ (const point& p1, const point& p2)    {        return p1.x*p2.y - p1.y*p2.x;    }};struct Segment{    point s, e;};///判断一个double类型的数是  0  <0  >0;int Sign(double x){    if( fabs(x) < eps )return 0;    if(x > 0)return 1;    return -1;}///判断o在ab的哪边;0:o在直线ab上; >0:在左边; <0:在右边;double cross(point o, point a, point b){    return ((a-o)^(b-o));}///已知abc三点在一条直线上,判断点a是否在线段bc之间;<=0:在   >0:不在;int Between(point a, point b, point c){    if(fabs(b.x-c.x) > fabs(b.y-c.y))        return Sign(min(b.x, c.x)-a.x)*Sign(max(b.x, c.x)-a.x);    else        return Sign(min(b.y, c.y)-a.y)*Sign(max(b.y, c.y)-a.y);}///判断点p0和线段S上,<=0:在,1:不在;int PointOnSegment(point p0, Segment S){    if(Sign(cross(S.s, S.e, p0)) == 0)        return Between(p0, S.s, S.e);    return 1;}///求线段a和线段b的交点个数;int SegmentCross(Segment a, Segment b){    double x1 = cross(a.s, a.e, b.s);    double x2 = cross(a.s, a.e, b.e);    double x3 = cross(b.s, b.e, a.s);    double x4 = cross(b.s, b.e, a.e);    if(Sign(x1*x2)<0 && Sign(x3*x4)<0) return 1;    if((Sign(x1)==0 && Between(b.s, a.s, a.e)<=0) ||       (Sign(x2)==0 && Between(b.e, a.s, a.e)<=0) ||       (Sign(x3)==0 && Between(a.s, b.s, b.e)<=0) ||       (Sign(x4)==0 && Between(a.e, b.s, b.e)<=0))       return 2;    return 0;}///判断点p0与含有n个节点的多边形的位置关系//p数组是顶点集合; 射线法判断点和多边形位置关系,多边形的点要求必须是逆时针描述. ///返回0:边上或顶点上,    1:外面,   -1:里面;int PointInPolygon(point p0, point p[], int n){    Segment L, S;    point temp;    L.s = p0, L.e = point(INF, p0.y);///以p0为起点的射线L;    int counts = 0;    p[n] = p[0];    for(int i=1; i<=n; i++)    {        S.s = p[i-1], S.e = p[i];        if(PointOnSegment(p0, S) <= 0) return 0;        if(S.s.y == S.e.y) continue;///和射线平行;        if(S.s.y > S.e.y) temp = S.s;        else temp = S.e;        if(PointOnSegment(temp, L) == -1)            counts ++;        else if(SegmentCross(L, S) == 1)            counts ++;    }    if(counts%2) return -1;    return 1;}int main(){    point p[N];    int T, tCase = 1, n, q;    scanf("%d", &T);    while(T--)    {        scanf("%d", &n);        for(int i=0; i<n; i++)            scanf("%lf %lf", &p[i].x, &p[i].y);        scanf("%d", &q);        printf("Case %d:\n", tCase++);        for(int i=1; i<=q; i++)        {            int x, y;            scanf("%d %d", &x, &y);            int ans = PointInPolygon(point(x, y), p, n);            if(ans == 1) puts("No");            else puts("Yes");        }    }    return 0;}

凸包

/*采用Graham扫描算法,复杂度nlogn.栈中保存点的顺序是按照逆时针顺序 */#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>#include <vector>using namespace std;const int MAXN = 50000 + 5;struct P{ int x, y; };P p[MAXN];int n;//求两点之间的距离 int dis(P a, P b){    return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);}/*计算叉积判断点和直线的方向用叉积若P1×p2 >0 则p1在p2的顺时针方向.<0 p1在p2的逆时针方向= 0 共线  */ int cross(P a, P b, P c){    return (a.x - c.x) * (b.y - c.y) - (b.x - c.x) * (a.y - c.y);}//极角排序 bool cmp(P a, P b){    int x = cross(p[0],a,b);    if(x > 0 || (x == 0 && dis(a, p[0]) < dis(b, p[0]))) return 1;    return 0;}vector<P> Graham(){    vector<P> stk(n);//存凸包    int k = 0;    //Graham 算法     for (int i = 0; i < n; i++) {        while (k > 1 && (cross(p[i], stk[k-2], stk[k-1])) <= 0)            k--;        stk[k++] = p[i];    }    stk.resize(k);//调整正确的凸包顶点数    return stk;}int main(){    while (~scanf("%d", &n)) {        int ans = 0, rp = 0;//rp用于临时保存最左下点的下标        //输入        for (int i = 0; i < n; i++) {            scanf("%d%d", &p[i].x, &p[i].y);            if(p[rp].y > p[i].y               || (p[rp].y == p[i].y && p[rp].x > p[i].x))//找最左下的点               rp = i;        }        //极角排序        swap(p[rp], p[0]);//初始化,方便cmp过程        sort(p + 1, p + n, cmp);        //Graham Scan构成凸包        vector<P> stk = Graham();        //暴力求解        for (int i = 0; i < stk.size(); i++) {            for (int j = 0; j < i; j++)                ans = max(ans, dis(stk[i], stk[j]));        }        cout<<ans<<endl;    }    return 0;}#include<iostream>#include<cstdio>#include<algorithm>using namespace std;typedef long long ll;const int mod=1e9+7;const int maxn=5e4+5;struct node{int x,y;}a[maxn],sta[maxn];//求亮点之间距离的平方. int dis(node n1,node n2){return (n1.x-n2.x)*(n1.x-n2.x)+(n1.y-n2.y)*(n1.y-n2.y);}//计算叉积 int det(int x1,int y1,int x2,int y2)  {      return x1*y2-x2*y1;  }  //判断点和直线的方向用叉积//若P1×p2 >0 则p1在p2的顺时针方向.//<0 p1在p2的逆时针方向//= 0 共线 int cross(node a,node n1,node n2){return det(n1.x-a.x,n1.y-a.y,n2.x-a.x,n2.y-a.y); }//极角排序 bool cmp(node n1,node n2){//a[0]为纵坐标最小的点一定在凸包上,以它为原点进行极角排序 int k = cross(a[0],n1,n2);if(k > 0) return 1;//角度小的排在前面 //若共线则距离a[0]距离近的在前面. if(k == 0 && dis(a[0],n1) < dis(a[0],n2))return 1;return 0;} void solve(node *ch,int len){int ans = 0;for(int i = 0;i <= len;++i){for(int j = i + 1;j <= len;++j){ans = max(ans,dis(ch[i],ch[j]));}}cout<<ans<<endl;}//最后栈中存的点就是凸包上的点. void Graham(int n){int i,head;for(i = 1;i < n;++i) //先找到最下面的那个点作为原点. if(a[i].x < a[0].x || (a[i].x == a[0].x && a[i].y < a[0].y))swap(a[0],a[i]); sort(a+1,a+n,cmp);//极角排序 a[n] = a[0];sta[0] = a[0];//最下面的一个点和第二个点一定在凸包上. sta[1] = a[1];sta[2] = a[2];head = 2;//Graham 算法 for(int i = 3;i < n;++i){while(head >= 2){int fu = cross(sta[head - 1],sta[head],a[i]);if(fu < 0) head--;else if(fu == 0 && dis(sta[head - 1],a[i]) > dis(sta[head - 1],sta[head])) head--;elsebreak;}sta[++head] = a[i];}solve(sta,head);}int main(){int n;while(scanf("%d",&n) > 0){for(int i = 0;i < n;++i){scanf("%d %d",&a[i].x,&a[i].y);}Graham(n);}return 0;}

叉积求多边形面积

/*叉积求多边形面积.(凹凸都适用)要求所有顶点按照逆时针顺序排列(点和多边形、凸包都是逆时针)由于叉积可能为负所以要取绝对值。因为每次除有误差,记得全部求完面积在除以2. */ double get_area(node *p,int len){double ans = 0;for(int i = 1;i < len;++i){ans += fabs(cross(p[0],p[i],p[i+1]));}return ans*0.5;}


原创粉丝点击