求锐角三角形个数.
统计锐角的个数,设为 A,钝角和直角的个数,设为 B
每个锐角三角形有三个锐角,每个钝角和直角三角形均贡献两个锐角
所以答案即为 A−2B3
然后题解在对每个点极角排序之后,采用 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; }