UVA 1342 That Nice Euler Circuit(二维几何基础)
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=528&page=show_problem&problem=4088
题意:
平面上有一个包含n个端点的一笔画,第n个端点总是和第1个端点重合,因此图案是一个闭合曲线.组成一笔画的各个线段可以相交,但是不会部分重叠.求平面被该一笔画分成了多少部分?
分析:
刘汝佳<<训练指南>>P260例题.
首先由欧拉定理 点-边+面=2. 所以我们只要求出该平面内的所有点和线段(边)数,就可以算出该平面被分成了多少部分.
那么我们先求所有的交点(不算n个端点,端点另外再加,所以这些交点一定是在线段内部的,即刘汝佳所说的规范相交): 由于一共n条线段,且任意线段不会部分重叠,所以任意两条线段要不平行(不相交)要不就规范相交. 所以我们只需要枚举所有的线段,然后求出他们规范相交的节点保存在数组中即可.
然后我们再把原本的n-1个顶点(起点与终点相同不用都加)加进去,然后去重.
下面我们要知道该平面一共有多少条线段,那么我们只要知道原来的每条线段到底被分成了多少段即可. 对于原来的每条线段,我们用上面求得的所有点去判断,当前这个点是不是在该线段内(不包含端点),如果该点在线段内,那么就说明该线段被该点截断,多出来1段.所以总的线段数目在n的基础上要+1.
最终求出了点和线段数,平面数= 边+2-点.
AC代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const double eps =1e-10; int dcmp(double x) { if(fabs(x)<eps) return 0; return x<0?-1:1; } struct Point { double x,y; Point(){} Point(double x,double y):x(x),y(y){} bool operator<(const Point &b)const { return x<b.x||(x==b.x && y<b.y); } bool operator==(const Point &b)const { return dcmp(x-b.x)==0 && dcmp(y-b.y)==0; } }; typedef Point Vector; Vector operator +(Vector A,Vector B) { return Vector(A.x+B.x, A.y+B.y); } Vector operator -(Point A,Point B) { return Vector(A.x-B.x, A.y-B.y); } Vector operator *(Vector A,double p) { return Vector(A.x*p, A.y*p); } Vector operator /(Vector A,double p) { return Vector(A.x/p, A.y/p); } double Dot(Vector A,Vector B) { return A.x*B.x+A.y*B.y; } double Length(Vector A) { return sqrt(Dot(A,A)); } double Angle(Vector A,Vector B) { return acos(Dot(A,B)/Length(A)/Length(B)); } double Cross(Vector A,Vector B) { return A.x*B.y-A.y*B.x; } bool SegmentProperIntersection(Point a1,Point a2,Point b1,Point b2)//判断线段规范相交 { double c1=Cross(a2-a1,b1-a1),c2=Cross(a2-a1,b2-a1); double c3=Cross(b2-b1,a1-b1),c4=Cross(b2-b1,a2-b1); return dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0; } bool OnSegment(Point P,Point a1,Point a2)//判断点P是否在选段a1a2内 { return dcmp( Cross(a1-P,a2-P) )==0 && dcmp(Dot(a1-P,a2-P))<0; } Point GetLineIntersection(Point P,Vector v,Point Q,Vector w)//求直线交点 { Vector u=P-Q; double t=Cross(w,u)/Cross(v,w); return P+v*t; } /***以上为刘汝佳模板***/ const int maxn=90000+5; Point p[maxn];//原始点 Point v[maxn];//所有点 int main() { int n; int kase=0; while(scanf("%d",&n)==1 && n) { int point_num=n-1;//点数目 int edge_num=n-1;//边数目 for(int i=0;i<n;++i) { scanf("%lf%lf",&p[i].x,&p[i].y); v[i]=p[i]; } --n; //求线段两两相交的交点 for(int i=0;i<n;++i) for(int j=i+1;j<n;++j) { Point &a1=p[i]; Point &a2=p[(i+1)%n]; Point &b1=p[j]; Point &b2=p[(j+1)%n]; if(SegmentProperIntersection(a1,a2,b1,b2)) { v[point_num++]=GetLineIntersection(a1,a2-a1,b1,b2-b1); } } sort(v,v+point_num); point_num = unique(v,v+point_num)-v; for(int i=0;i<n;++i)//原始线段 for(int j=0;j<point_num;++j)//遍历所有点 { if(OnSegment(v[j],p[i],p[(i+1)%n])) ++edge_num;//若该点在该线段内部 } printf("Case %d: There are %d pieces.\n",++kase,edge_num+2-point_num); } return 0; }