平面图中的欧拉定理

定理:设G为任意的连通的平面图,则v-e+f=2,v是G的顶点数,e是G的边数,f是G的面数。

题目描述:给出一个一笔画图形的n个节点的坐标,请你求解这个图形把平面分成了几个面
一笔画图形一个是把图上所有的边仅且遍历一次的封闭连通图。
分析:求面的问题就分解为求顶点个数与边条数的问题。
点的求法:遍历每条线段,如果是规范相交,则将该点存入V[]数组中。最后对V[]数组进行排序,使用STL中的unique()函数对求出来的交点进行去重处理。
边的求法:遍历每个交点和每条线段,如果交点在线段上,边数加一

//欧拉定理
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string.h>
#include <vector>

using namespace std;

struct point
{
    double x, y;
    point(double x = 0.0, double y = 0.0) :x(x), y(y) {};
};
typedef point Vector;

const double eps = 1e-8;
//判断double类型(x)与0的大小
int dcmp(double x) { if (fabs(x)<eps) return 0; return (x<0) ? -1 : 1; }

//向量的运算符重载
Vector operator + (Vector A, Vector B) { return Vector(A.x + B.y, A.y + B.y); }
Vector operator - (Vector A, Vector B) { return Vector(A.x - B.y, A.y - B.y); }
Vector operator * (Vector A, double p) { return Vector(p * A.x, p * A.y); }
Vector operator / (Vector A, double p) { return Vector(A.x / p, A.y / p); }
bool operator < (const point& a, const point& b) {//判断a点是否小于b点
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}

bool operator == (const point& a, const point &b) {//判断两个点是否相等
    return dcmp(a.x - b.x) == 0 && dcmp(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; }//向量的叉积

 //判断规范相交
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;
}

//两直线交点(参数法)
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;
}
//判断点p是否在线段a1a2上(叉乘=0;点乘<0)
bool OnSegment(point p, point a1, point a2)
{
    return dcmp(Cross(a1 - p, a2 - p)) == 0 && dcmp(Dot(a1 - p, a2 - p))<0;
}
const int maxn = 300 + 10;
point P[maxn], V[maxn * maxn];
int main()
{
    int n, kase = 0, c, e;
    while (~scanf("%d", &n) && n) {
        for (int i = 0; i < n; i++) {
            scanf("%d%d", &P[i].x, &P[i].y);
            V[i] = P[i];
        }
        c = n - 1; e = n - 1;
        for (int i = 0; i < c; i++)//遍历每个点
            for (int j = i + 1; j < c; j++)
                if (SegmentProperIntersection(P[i], P[i + 1], P[j], P[j + 1]))//如果是规范相交
                    V[c++] = GetLineIntersection(P[i], P[i + 1] - P[i], P[j], P[j + 1] - P[j]);//把交点存入V[]中

        sort(V, V + c);
        c = unique(V, V + c) - V;//把重复元素放到数组后面,c为(去重后)交点的个数
        for (int i = 0; i < c; i++)//遍历每个交点
            for (int j = 0; j < n - 1; j++)//遍历每条线段
                if (OnSegment(V[i], P[j], P[j + 1])) e++;//如果交点在线段上,边数加一
        printf("Case %d: There are %d pieces.\n", ++kase, e + 2 - c);
    }
}
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值