UVA 1342 That Nice Euler Circuit(二维几何基础)
题意:
平面上有一个包含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;
}