计算几何 + 欧拉定理 (一笔画)

摘要:
主题大意:依次给出几个点,连接前后点,以找到最终获得的图形的面数。思想分析:借助于欧拉定理,V+F-E=2,只需要点的数量和边的数量就可以计算面的数量。点的数量可以通过枚举直线来计算,但可能存在重复点,并且可以消除重复最多的点。然后,其余的点在枚举中以几条直线显示。

题目大意:
依次给定多个点(要求第一个点和最后一个点重叠),把前后两个点相连求最后得到的图形的面的个数

思路分析 :

借助欧拉定理,V+F-E = 2,只要求出点的数量和边的数量就可以计算出面的数量,点的数量只要枚举直线就可以,但是有可能有重复的点,之最去重一下就可以,然后在枚举剩下的点出现在几条直线中。

代码示例:(未测试)

#define ll long long
const double eps = 1e-10;
const double pi = acos(-1.0);

struct point
{
    double x, y;
    point(double _x, double _y):x(_x), y(_y){}
    
    // 点-点=向量
    point operator-(const point &v){
        return point(x-v.x, y-v.y);
    }

};

int dcmp(double x){
    if (fabs(x)<eps) return 0;
    else return x<0?-1:1;
}
bool operator == (const point &a, const point &b){
    return (dcmp(a.x-b.x)==0 && dcmp(a.y-b.y)==0); 
}

typedef point Vector; // Vector表示向量

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

//线段的长度
double Lenth(Vector a){return sqrt(Dot(a, a));}

//向量的夹角(弧度)
double Angle(Vector a, Vector b){return acos(Dot(a, b)/Lenth(a)/Lenth(b));}

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

//三角形有向面积的2倍
double Area2(point a, point b, point c){return Cross(b-a, c-a);}

//向量旋转,a为逆时针旋转的角度
// rad是弧度
point Rotate(Vector a, double rad){
    return point(a.x*cos(rad)-a.y*sin(rad), a.x*sin(rad)+a.y*cos(rad));
}

//计算向量的单位法线
point Normal(Vector a){
    double L = Lenth(a);
    return point(-a.y/L, a.x/L);
}

// 两条直线的交点
// 两条直线为 p+tv, q+tw,其中p, q为两直线上的一点, v,w是直线所在方向的向量,
// 交点在第一条直线的参数为t1, 第二条直线的参数为t2
// t1 = (cross(w,u)/cross(v,w));  t2 = (cross(v,u)/cross(v,w));
// 调用前要确保两直线有唯一交点,当且仅当两直线不共线
point Getline(point p, Vector v, point q, Vector w){
    point u = p-q;
    double t = Cross(w, u)/Cross(v, w);
    return point(p.x+v.x*t, p.y+v.y*t);
}

//点到直线的距离
double Dis(point p, point a, point b){
    Vector v1 = b-a, v2 = p-a;
    return fabs(Cross(v1, v2)/Lenth(v1));
}

//点到线段的距离
//点p在直线的投影可能在直线上,也可能不在直线上
double Dis2(point p, point a, point b){
    if (a==b) return Lenth(p-a);
    Vector v1=b-a, v2 = p-a, v3=p-b;
    if (dcmp(Dot(v1, v2))<0) return Lenth(v2); // 注意大小于号
    if (dcmp(Dot(v1, v3))>0) return Lenth(v3);
    else return fabs(Cross(v1, v2)/Lenth(v1));
}

//点在线段上的投影
point Getline2(point p, point a, point b){
    Vector v = b-a;
    double f = Dot(v, p-a)/Dot(v, v);
    return point(a.x+v.x*f, a.y+v.y*f);
}

//线段相交判定(交点不在端点的位置,且两直线仅有唯一交点)
bool Inter(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 dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0;
}
//线段相交判定,判定端点是否在另一条直线上(交点不在两直线端点得位置)
bool OneInter(point p, point a1, point a2){
    return dcmp(Cross(a1-p, a2-p))==0 && dcmp(Dot(a1-p, a2-p))<0;
}
// 视题目要求是否特判两直线有端点重合的情况

vector<point>p, v;
bool cmp(point a, point b){
    if (a.x == b.x) return a.y<b.y;
    else return a.x<b.x; 
}
int main() {
    int n;
    double x, y;
    
    cin >> n;
    for(int i = 1; i <= n; i++){
        scanf("%lf%lf", &x, &y);
        p.push_back(point(x, y));
        v.push_back(point(x, y));
    }
    v.push_back(point(x, y));
    n--; // 最后一个点是起点    
    for(int i = 0; i < n; i++){
        for(int j = i+1; j < n; j++){
            if (Inter(p[i], p[i+1], p[j], p[j+1])) {
                v.push_back(Getline(p[i], p[i+1]-p[i], p[j], p[j+1]-p[j]));
            }
        }
    }
    sort(v.begin(), v.end(), cmp);
    v.erase(unique(v.begin(), v.end()), v.end());
    int c = v.size();
    int e = n;
    for(int i = 0; i < c; i++){
        for(int j = 0; j < n; j++){
            if (OneInter(v[i], p[j], p[j+1])) e++;
        }
    }
    printf("%d
", e-c+2);
    return 0;
}

免责声明:文章转载自《计算几何 + 欧拉定理 (一笔画)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇ClickHouse源码笔记3:函数调用的向量化实现windows下使用命令行安装nessus下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Android 开发 VectorDrawable 矢量图 (八)animation-list帧动画配合矢量图实现动画

前言  只是矢量图的一个使用小技巧,关键点是<aapt:attr name="android:drawable"> 属性,它其实是代替<item 里的android:drawable属性。理解它,你可以举一反三使用到更多的需要实现动画,按下效果,选择效果的xml文件上,比如一个实现按下效果的xml将原来需要3个xml文件合并成一个xml文...

C++中vector 容器的基本操作

vector是一种简单高效的容器,具有自动内存管理功能。对于大小为n的vector容器,它的元素下标是0~n-1。vector有二个重要方法:begin(): 返回首元素位置的迭代器。end(): 返回最后一个元素的下一个元素位置的迭代器。1、 vector对象创建的几种方式。1)不指定容器元素个数。vector<double> v;2)指定容...

访问vector元素方法的效率比较(转)

LInux下: gcc 4.47,red hat6 1 #include<iostream> 2 #include<vector> 3 #include<time.h> 4 using namespace std; 5 6 7 8 int main() { 9 //建立4个...

OpenCASCADE 参数曲面面积

OpenCASCADE 参数曲面面积 eryar@163.com Abstract. 本文介绍了参数曲面的第一基本公式,并应用曲面的第一基本公式,结合OpenCASCADE中计算多重积分的类,对任意参数曲面的面积进行计算。 Key Words. Parametric Curve, Parametric Surface, Gauss Integration,...

堆积木----vector防止内存超限

蒜头君有 nn 块积木,编号分别为 11 到 nn。一开始,蒜头把第 ii 块积木放在位置 ii。蒜头君进行 mm 次操作,每次操作,蒜头把位置 bb 上的积木整体移动到位置 aa 上面。比如 11 位置的积木是 11,22 位置的积木是 22,那么把位置 22 的积木移动到位置 11 后,位置 11 上的积木从下到上依次为 1,21,2。 输入格式 第...

使用C++ 实现的 websocket 客户端 (基于easywsclient)

直接上代码 easywsclient.hpp #ifndef EASYWSCLIENT_HPP_20120819_MIOFVASDTNUASZDQPLFD #define EASYWSCLIENT_HPP_20120819_MIOFVASDTNUASZDQPLFD // This code comes from: // https://github.co...