`
阿尔萨斯
  • 浏览: 4179453 次
社区版块
存档分类
最新评论

UVA 1342 That Nice Euler Circuit(二维几何基础)

 
阅读更多

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;
}

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics