【LA3263】That Nice Euler Circuit——欧拉定理的应用

Little Joey invented a scrabble machine that he called Euler, after the great mathematician. In his
primary school Joey heard about the nice story of how Euler started the study about graphs. The
problem in that story was – let me remind you – to draw a graph on a paper without lifting your
pen, and finally return to the original position. Euler proved that you could do this if and only if the
(planar) graph you created has the following two properties: (1) The graph is connected; and (2) Every
vertex in the graph has even degree.
Joey’s Euler machine works exactly like this. The device consists of a pencil touching the paper,
and a control center issuing a sequence of instructions. The paper can be viewed as the infinite
two-dimensional plane; that means you do not need to worry about if the pencil will ever go off the
boundary.
In the beginning, the Euler machine will issue an instruction of the form (X0, Y 0) which moves the
pencil to some starting position (X0, Y 0). Each subsequent instruction is also of the form (X′, Y ′),
which means to move the pencil from the previous position to the new position (X′, Y ′), thus draw a line segment on the paper. You can be sure that the new position is different from the previous position for each instruction. At last, the Euler machine will always issue an instruction that move the pencil back to the starting position (X0, Y 0). In addition, the Euler machine will definitely not draw any lines that overlay other lines already drawn. However, the lines may intersect.
After all the instructions are issued, there will be a nice picture on Joey’s paper. You see, since the
pencil is never lifted from the paper, the picture can be viewed as an Euler circuit.
Your job is to count how many pieces (connected areas) are created on the paper by those lines
drawn by Euler.

Input

There are no more than 25 test cases. Ease case starts with a line containing an integer N ≥ 4, which
is the number of instructions in the test case. The following N pairs of integers give the instructions
and appear on a single line separated by single spaces. The first pair is the first instruction that gives
the coordinates of the starting position. You may assume there are no more than 300 instructions in
each test case, and all the integer coordinates are in the range (-300, 300). The input is terminated
when N is 0.

Output

For each test case there will be one output line in the format
Case x: There are w pieces.,
where x is the serial number starting from 1.
Note: The figures below illustrate the two sample input cases.

Sample Input
5
0 0 0 1 1 1 1 0 0 0
7
1 1 1 5 2 1 2 5 5 1 3 5 1 1
0

Sample Output

Case 1: There are 2 pieces.
Case 2: There are 5 pieces.

题意:给你一个闭合的曲线,计算这个曲线有多少个平面。

分析:如果直接找所有的区域会比较的麻烦,但是可以利用欧拉定理来进行计算。
欧拉定理:设平面的顶点数,边数,面数为V,E,F,则V+F-E = 2,即F = E+2-V。
所以我们只要求出顶点数和边数就可以求出面数。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <algorithm>
#include <iostream>

using namespace std;

const double eps = 1e-6;

const double PI = acos(-1.0);

//################################################################### 二维几何

typedef struct Point
{
    double x,y;

    Point(double x= 0 ,double y = 0 ):x(x),y(y){}

    Point operator + (const Point &a)const {return Point(a.x+x,y+a.y);}

    Point operator - (const Point &a)const {return Point(x-a.x,y-a.y);}

    Point operator * (const double &a)const {return Point(x*a,y*a);}

    Point operator / (const double &a)const {return Point(x/a,y/a);}

    bool operator < (const Point &a)const {return x<a.x||(x==a.x&&y<a.y);}


}Vector;

int dbcmp(double x)
{
    if(fabs(x)<eps) return 0;

    return x>0?1:-1;
}

bool operator == (const Point &a,const Point  b) //判断点的相等
{
    return dbcmp(a.x-b.x)==0 && dbcmp(a.y-b.y)==0;
}

double Dot(Vector A,Vector B) {return A.x*B.x+A.y*B.y;} //点积

double Cross(Vector A,Vector B) {return A.x*B.y-A.y*B.x;} //叉积

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 Area2(Point A,Point B,Point C) {return Cross(B-A,C-A);} //求平行四边形面积

Vector Rotate (Vector A,double rad) {return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad));} //二维向量逆时针旋转rad

Point GetLineIntersection(Point P,Vector v,Point Q,Point w) //求两直线Pv,Qw交点,保证直线只有唯一的一个交点。
{
    Vector u = P-Q;

    double t = Cross(w,u)/Cross(v,w);

    return P+v*t;
}

double DistanceToLine(Point P,Point A,Point B) //点P到直线AB的距离
{
    Vector v1 = B-A,v2 = P - A;

    return fabs(Cross(v1,v2)/Length(v1));
}

double DistanceToSegment(Point P,Point A,Point B) //点到线段的最短距离
{
    if(A == B) return Length(P-A);


    Vector v1 = B - A,v2 = P-A,v3 = P-B;

    if(dbcmp(Dot(v1,v2))<0) return Length(v2);//在A端点的外面

    else if(dbcmp(Dot(v1,v3))>0) return Length(v3);//在B端点的外面

    else return fabs(Cross(v1,v2)/Length(v1));
}

Point GetLineProjection(Point P,Point A,Point B) //点在线段上的投影的坐标
{
    Vector v = B-A;

    return A+v*(Dot(v,P-A)/Dot(v,v));
}

bool SegmentProperIntersection(Point a1,Point a2,Point b1,Point b2) //判断线段规范相交
{
    double c1 = Cross(a2-a1,b1-a1),c2 = Cross(a2-a1,b2-a1),c3 = Cross(b2-b1,a1-b1),c4 = Cross(b2-b1,a2-b1);

    return dbcmp(c1)*dbcmp(c2)<0&&dbcmp(c3)*dbcmp(c4)<0;
}

bool OnSegment(Point P,Point A,Point B) { return dbcmp(Cross(A-P,B-P)) == 0 && dbcmp(Dot(A-P,B-P)) < 0;} //判断点P是不是在线段AB上

double PolygonArea(Point *p,int n)//求多边形的有向面积
{
    double Area = 0;

    for(int i = 1;i < n-1; i++) Area += Cross(p[i]-p[0],p[i+1]-p[0]);

    return Area/2;
}

Point GetMorley(Point A,Point B,Point C)//Morley定理
{
    Vector v1  = C-B;

    double a1 = Angle(A-B,v1);

    v1 = Rotate(v1,a1/3);



    Vector v2 = B-C;


    double a2 = Angle(A-C,v2);

    v2 = Rotate(v2,-a2/3);

    return GetLineIntersection(B,v1,C,v2);
}

int n;

Point P[400];

Vector V[400*400];

int main()
{
    int num ;

    int z = 1;

    while(scanf("%d",&n)&&n)
    {
        for(int i =0 ; i<n;i++)
        {
            scanf("%lf %lf",&P[i].x,&P[i].y);

            V[i] = P[i];
        }

        n--;
        num = n;

        for(int i= 0 ; i<n;i++)//计算所有的顶点
        {
            for(int j = i+1;j<n;j++)
            {
                if(SegmentProperIntersection(P[i],P[i+1],P[j],P[j+1]))
                {
                    V[num++] = GetLineIntersection(P[i],P[i+1]-P[i],P[j],P[j+1]-P[j]);
                }
            }
        }

        sort(V,V+num);

        int ans = n;

        num = unique(V,V+num)-V;//由于存在共点的情况,所以要去重。
        for(int i = 0 ;i<num;i++)//根据点是不是在线段上,判断边的数量。
        {
            for(int j = 0;j<n;j++)
            {
                if(OnSegment(V[i],P[j],P[j+1]))
                {
                    ans++;
                }
            }
        }

        printf("Case %d: There are %d pieces.\n",z++,ans+2-num);//根据欧拉定理求解。

    }
    return 0;
}

你可能感兴趣的:(【LA3263】That Nice Euler Circuit——欧拉定理的应用)