1、角度和判别(无论是凹三角形还是凸三角形都适用,点在边上不认为是在多边形内)
假如一个点在多边形内,那么该点与多边形的每相邻的两个点形成的角度和一定是刚好等于360°的(在代码实现过程中要使用弧度制,即等于2*pi),否则就在多边形外面。
在输入多边形点的时候要按顺时针方向或者是逆时针方向输入
下图详细说明
图1 图二
图一:前面所说的角度和即∠APB+∠BPC+∠CPD+∠DPE+∠EPA,显而易见角度和为360°;图二的角度和与图一一样,但是角度和却不是360°;
因此,根据角度和完全可以判断出点是否在多边形内
代码:
const double pi = acos(-1); struct point { double x; double y; }p[101],p1[101]; typedef point Vect; struct segment { point p1; point p2; }; point operator -(point a,point b) { point x; x.x = a.x - b.x; x.y = a.y - b.y; return x; } double mo(Vect v) { return sqrt(v.x * v.x + v.y * v.y); }//求模长 double angle(Vect v1,Vect v2) { double ans = acos((v1.x * v2.x + v1.y * v2.y) / (mo(v1) * mo(v2))); return ans; }//根据余弦定理求角度 bool inpolygon(point p,point a[],int n) { double sum = 0; for(int i = 0;i < n;i++) { point v1 = a[i] - p; point v2 = a[(i + 1) % n] - p; sum += angle(v1,v2); } if(fabs(sum - 2 * pi) < 1e-8) return false; else return true; }//判断角度和是否等于2*pi
二、跨立实验(仅适用于凸多边形)
点在边上会认为也在多边形内
设这个多边形的边数为n。选一条边作为1号边,然后按照顺时针或者逆时针给每条边编号,连接第i(i=1,2,3......,n)条边的第一个端点和要测试的点得到一个向量vi。连接第一个端点与第二个端点得到向量ui,都以第一个端点为起点,作叉积vi*ui,记录结果。除了第一条边以外,都与前一次运算得到的叉积作乘积。如果为正则继续判断,知道遍历所有边,若全部满足,则证明点在多边形内,否则,证明点在多边形外。
代码:
const int maxn = 110; int t; struct point { double x; double y; } a[maxn],b[maxn]; double mult(point a,point b,point c) { return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y); }//向量叉积 bool isinside(point p,point a[],int n) { double pre,now; for(int i = 0; i < n; i++) { now = mult(p,a[i],a[(i + 1) % n]); if(i > 0) { if(pre * now < 0) { return 0; } } pre = now; } return true; }//判断点是否在凸多边形内
贴上几个模板题:多边形的公共部分(湖南省第十一届大学生计算机程序设计竞赛)
http://acm.csu.edu.cn:20080/csuoj/problemset/problem?pid=1778
解题思路:由于题目明确给出两条边重合并不算有公共部分,因此直接使用角度判别法
代码:
#include <bits/stdc++.h> #define pi acos(-1) using namespace std; int t; int n,m; struct point { double x; double y; }p[101],p1[101]; typedef point Vect; struct segment { point p1; point p2; }; point operator -(point a,point b) { point x; x.x = a.x - b.x; x.y = a.y - b.y; return x; } double mo(Vect v) { return sqrt(v.x * v.x + v.y * v.y); } double angle(Vect v1,Vect v2) { double ans = acos((v1.x * v2.x + v1.y * v2.y) / (mo(v1) * mo(v2))); return ans; } bool inpolygon(point p,point a[],int n) { double sum = 0; for(int i = 0;i < n;i++) { point v1 = a[i] - p; point v2 = a[(i + 1) % n] - p; sum += angle(v1,v2); } if(fabs(sum - 2 * pi) < 1e-8) return false; else return true; } int main() { int cou = 1; while(scanf("%d",&n) != EOF) { for(int i = 0;i < n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); scanf("%d",&m); int flag = 0; for(int i = 0;i < m;i++) { scanf("%lf%lf",&p1[i].x,&p1[i].y); if(!inpolygon(p1[i],p,n)) flag = 1; } printf("Case %d: ",cou++); if(flag == 1) printf("Yes\n"); else printf("No\n"); } return 0; }
题目:Triangles
http://fastvj.rainng.com/problem/FZU-2273
解题思路:由于这个题要判断三种情况,并且只要有公共点就会认为是相交,因此使用跨立实验判别,另外还要考虑一个问题就是就算两个三角形的点都在另一个三角形的外部也有可能会相交,如图所示
加入一个判断线段相交函数解决问题
bool intersect(point aa, point bb, point cc, point dd)//相交返回true,不相交返回false { if(max(aa.x,bb.x) < min(cc.x,dd.x)) return false; if(max(aa.y,bb.y) < min(cc.y,dd.y)) return false; if(max(cc.x,dd.x) < min(aa.x,bb.x)) return false; if(max(cc.y,dd.y) < min(aa.y,bb.y)) return false; if(mult(cc,bb,aa) * mult(bb,dd,aa) < 0) return false; if(mult(aa,dd,cc) * mult(dd,bb,cc) < 0) return false; return true; }//判断两线段是否相交
完整代码:
#include <iostream> #include <cstdio> using namespace std; const int maxn = 110; int t; struct point { double x; double y; } a[3],b[3]; double mult(point a,point b,point c) { return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y); }//向量叉积 bool isinside(point p,point a[]) { double pre,now; for(int i = 0; i < 3; i++) { now = mult(p,a[i],a[(i + 1) % 3]); if(i > 0) { if(pre * now < 0) { return 0; } } pre = now; } return true; }//判断点是否在凸多边形内 bool intersect(point aa, point bb, point cc, point dd)//相交返回true,不相交返回false { if(max(aa.x,bb.x) < min(cc.x,dd.x)) return false; if(max(aa.y,bb.y) < min(cc.y,dd.y)) return false; if(max(cc.x,dd.x) < min(aa.x,bb.x)) return false; if(max(cc.y,dd.y) < min(aa.y,bb.y)) return false; if(mult(cc,bb,aa) * mult(bb,dd,aa) < 0) return false; if(mult(aa,dd,cc) * mult(dd,bb,cc) < 0) return false; return true; }//判断两线段是否相交 int main() { scanf("%d",&t); while(t--) { for(int i = 0; i < 3; i++) scanf("%lf%lf",&a[i].x,&a[i].y); int cou = 0; int num = 0; for(int i = 0; i < 3; i++) { scanf("%lf%lf",&b[i].x,&b[i].y); if(isinside(b[i],a)) { cou++; } } for(int i = 0; i < 3; i++) if(isinside(a[i],b)) num++; int flag = 0; for(int i = 0; i < 3; i++) { for(int j = 0; j < 3; j++) { if(intersect(a[i],a[(i + 1) % 3],b[j],b[(j + 1) % 3])) { flag = 1; break; } } if(flag == 1) break; } if((cou == 3 || num == 3) && flag == 0) printf("contain\n"); else if((cou > 0 && cou < 3) || flag == 1) printf("intersect\n"); else printf("disjoint\n"); } return 0; }