计算几何模板



求锐角三角形个数.

统计锐角的个数,设为 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--;
				else
				break;
			}
			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;
}

叉积求多边形面积

扫描二维码关注公众号,回复: 907419 查看本文章

/*
叉积求多边形面积.(凹凸都适用)
要求所有顶点按照逆时针顺序排列(点和多边形、凸包都是逆时针)
由于叉积可能为负所以要取绝对值。
因为每次除有误差,记得全部求完面积在除以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;
}


猜你喜欢

转载自blog.csdn.net/howardemily/article/details/77783074