zoukankan      html  css  js  c++  java
  • 计算几何模板(一)

    这两周一直在做计算几何的题,感觉计算几何题就是读懂题意,并且能有清晰的解题思路,剩下的就是套用模板了,所以模板相当重要!下面整理了一些模板以便以后做题用

    说明:下面的eps 都是1e-8;

    目录:

    ㈠ Pick定理:

    ㈡ 在二维空间中,已知四个点的坐标,求交点的坐标

    ㈢ 已知多边形的顶点的坐标,求多边形的面积利用叉积

    ㈣ 求三角形面积

    ㈤ 判断一个多边形是否是一个凸包

    ㈥ 求凸包的周长(在若干个点钟选取点组成凸包并求其周长)

    ㈦ 判断点是否在凸包内

    ㈧ 凸包内的点到凸包每条边的距离

    ㈨ 跨立试验--判断两条线段是否相交(含顶点)

    ㈩ 判断点是否在矩形的内部

    ①.Pick定理:如图给定顶点坐标都是整点,所围成的多边形的面积area、多边形边上的点的个数on以及多边形内部点的个数in满足area = in + on / 2 – 1;

    POJ 1265 Area

    代码如下:

    View Code
    int m, n, on, in;
    double area;
    struct node
    {
        int x, y;
    }point[MAX];
    int Gcd(int a, int b)//求a和b的最大公约数的函数
    {
        int temp;
        if (a < b)//把a和b中较大的数字放到a中
        {
            temp = a;
            a = b;
            b = temp;
        }
        if (b == 0)//找到两者的最大公约数,直接返回a
        {
            return a;
        }
        return Gcd(b, a % b);//否则继续往下找
    }
    int Area(int i)//叉积计算多边形的面积
    {
        return double(point[i - 1].x * point[i].y - point[i].x * point[i - 1].y);
    }
    int main()
    {
        int i, j, xx, yy;
        scanf("%d", &m);
        for (i = 1; i <= m; ++i)
        {
            printf("Scenario #%d:\n", i);
            scanf("%d", &n);
            memset(point, 0, sizeof(point));
            on = 0;
            in = 0;
            area = 0;
            for (j = 1; j <= n; ++j)
            {
                scanf("%d%d", &xx, &yy);
                point[j].x = xx + point[j - 1].x;
                point[j].y = yy + point[j - 1].y;
                on += Gcd(abs(xx), abs(yy));//求在边上的点的个数
                area += Area(j);
            }
            area = area / 2.0;
            in = int(area) + 1 - on / 2;
            printf("%d %d %.1lf\n", in, on, area);
            printf("\n");
        }
        return 0;
    }

    ②在二维空间中,已知四个点的坐标,求交点的坐标

    核心代码如下:

    Point GetPoint(Point p1, Point p2, Point p3, Point p4)//已知四个点求直线相交的点的坐标
    //(这种求法包括了直线斜率不存在或为0的情况下)
    {
        Point p0;//p0就是所求的交点的坐标
        double A1 = p2.y - p1.y;
        double B1 = p1.x - p2.x;
        double C1 = p1.y * (-B1) - p1.x * A1;
        double A2 = p4.y - p3.y;
        double B2 = p3.x - p4.x;
        double C2 = p3.y *(-B2) - p3.x * A2;
        p0.x = (C2 * B1 - C1 * B2) / (A1 * B2 - A2 * B1);
        p0.y = (C2 * A1 - C1 * A2) / (B1 * A2 - B2 * A1);
        return p0;
    }

    ③已知多边形的顶点的坐标,求多边形的面积利用叉积

    核心代码实现如下:

    for (int i = 0; i < N; ++i)
    {
    int j = (i + 1) % N;
    area += point[i].x * point[j].y - point[i].y * point[j].x;
    }
    area = area / 2;
    if (area < 0)
    {
    area = - area;
    }

    ④求三角形的面积

    ⑴   利用的是海伦公式求的三角形面积(已知三角形的三个顶点的坐标)

    原理假设有一个三角形,边长分别为a、b、c,三角形的面积Area可由以下公式求得:设p是周长的一半,p=(a+b+c)/2,则三角形的面积为:Area =sqrt〔p*(p-a)*(p-b)*(p-c)〕

    代码如下:

    double GetArea(int m, Point tr[])
    {
        double a[m];
        double pp = 0;
        for (int i = 0; i < m; ++i)
        {
            a[i] = Dis(tr[i], tr[(i + 1) % m]);
            pp += a[i];
        }
        pp = pp / 2.0;
        return sqrt(pp * (pp - a[0]) * (pp - a[1]) * (pp -a[2]));
    }
    ⑵   利用叉积求三角形面积(已知三角形的三个顶点的坐标)

    代码如下:

    double GetArea(int m, Point tr[])//其中这儿的m就是3了
    {
        int i;
        double area = tr[0].y * (tr[m - 1].x - tr[1].x);
        for (i = 1; i < m; ++i)//求三角形的面积
        {
            area += tr[i].y * (tr[i - 1].x - tr[(i + 1) % m].x);
        }
        if (area < 0)
        {
            area = -area;
        }
        return area = area / 2.0;
    }

    ⑤判断一个多边形是否是一个凸包(有若干个点是否能构成凸包)

    核心代码如下:

    for (i = 0; i + 2 < n; ++i)
    {
         dis = Multi(p[i + 1], p[i + 2], p[i]);
         if (abs(dis) >= eps)//先确定多边形的输入是逆时针还是顺时针
         {
               break;
          }
    }
    flag = 1;
    for (i = 0; i < n && flag; ++i)
    {
        for (j = 0, k = i + 2; j < n - 2; j ++, k ++)
        {
             double q = Multi(p[(i + 1) % n], p[k % n], p[i]);
             if (q * dis < -eps)//出现了旋转方向相反的点了
             {
                  flag = 0;
                     break;
            }
        }
     }
     if (flag == 0)//不是凸包
    {
          printf("不是凸包\n");
     }

    ⑥求凸包的周长(在若干个点钟选取点组成凸包并求其周长)

    核心代码如下:

    int convex[MAX];//储存选取的点
    bool cmp(Point a, Point b)//先按y的从小到大排序,若y的大小相等的时候,再按x从小到大排序!这个函数在#include<algorithm>头文件中
    {
        if(a.y == b.y) return a.x < b.x;
        return a.y < b.y;
    }
    double Multi(Point p1, Point p2, Point p3)
    {
       return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
    }
    void Graham()//选取点构成凸包
    {
        int i, count;
        top = 1;
        sort(p, p + N, cmp);//排序函数
        for (i = 0; i < 3; ++i)//先把前三个点压入栈中
        {
            convex[i] = i;
        }
        for (i = 2; i < N; ++i)
        {
            while (top && Multi(p[i], p[convex[top]], p[convex[top - 1]]) >= 0)
            {
                top --;
            }
            convex[++ top] = i;
        }
        count = top;
        convex[++ top] = N - 2;
        for (i = N - 3; i >= 0; --i)
        {
            while (top != count && Multi(p[i], p[convex[top]], p[convex[top - 1]]) >= 0)
            {
                top --;
            }
            convex[++ top] = i;
        }
    }
    for (i = 0; i < top; ++i)//求周长
    {
          ans += Dis(convex[i], convex[(i + 1) % top]);
    }
     printf("%.0lf\n", ans);

    ⑦判断点是否在凸包内

    int Judge()
    {
        double a, b;
        int i;
        Point p3;
        p3.x = x;
        p3.y = y;
        b = Multi(p[0], p[1], p3);
        for (i = 1; i < n; ++i)
        {
            a = Multi(p[i], p[(i + 1) % n], p3);
            if (a * b < - eps)
            {
                return 1;//说明点(x, y)不在凸包内
            }
            b = a;
        }
        return 0;
    }

    ⑧凸包内的点到凸包每条边的距离

    实现的原理是:求到多边形每条边的距离利用了三角形的面积 = (底边的边长 * 高) / 2;即 dis = 2.0 * area / h;

    triangle[0].x = x;
    triangle[0].y = y;//(x, y)是凸包内的一个点
    for (i = 0; i < n; ++i)
    {
         triangle[1] = p[i];
         triangle[2] = p[(i + 1) % n];
         dis = 2.0 * GetArea(3, triangle) / Dis(triangle[1], triangle[2]);
         printf("%.2lf\n", dis);
    }

    ⑨跨立试验--判断两条线段是否相交(含顶点)

    bool Isintersect(Point a1, Point a2, Point b1, Point b2)//判断两条线段是否相交(含顶点)
    {
        if (Min(a1.x, a2.x) <= Max(b1.x, b2.x) &&
            Min(a1.y, a2.y) <= Max(b1.y, b2.y) &&
            Min(b1.x, b2.x) <= Max(a1.x, a2.x) &&
            Min(b1.y, b2.y) <= Max(a1.y, a2.y) &&
            Multi(a1, a2, b1) * Multi(a1, a2, b2) <= 0 &&
            Multi(b1, b2, a1) * Multi(b1, b2, a2) <= 0
            )
            return true;//说明两线段之间相交
        return false;
    }

    ⑩判断点是否在矩形的内部

    其中seg是代表点,rec[1],rec[3]代表了矩形的对角点
    bool Inrectangle(int i)//判断点是否在矩形的内部
    {
        if (seg[i].x > Max(rec[1].x, rec[3].x)) return false;
        if (seg[i].y > Max(rec[1].y, rec[3].y)) return false;
        if (seg[i].x < Min(rec[1].x, rec[3].x)) return false;
        if (seg[i].y < Min(rec[1].y, rec[3].y)) return false;
        //如果满足上面的,说明线段在矩形的内部并且没有和矩形没有交点(含线段的顶点)
        return true;
    }
    ⑾    已知两条相交直线,知道每条直线的的两个点,求交点的坐标
    Point GetPoint(Point p1, Point p2, Point p3, Point p4)//已知四个点求直线相交的点的坐标
    //(这种求法包括了直线斜率不存在或为0的情况下)
    {
        Point p0;
        double A1 = p2.y - p1.y;
        double B1 = p1.x - p2.x;
        double C1 = p1.y * (-B1) - p1.x * A1;
        double A2 = p4.y - p3.y;
        double B2 = p3.x - p4.x;
        double C2 = p3.y *(-B2) - p3.x * A2;
        p0.x = (C2 * B1 - C1 * B2) / (A1 * B2 - A2 * B1);
        p0.y = (C2 * A1 - C1 * A2) / (B1 * A2 - B2 * A1);
        return p0;
    }
  • 相关阅读:
    java-五子棋棋盘排布 && 数字转换成汉字 &&大数计算
    动手动脑,无法自拔(2)课时作业4
    动手动脑,无法自拔(1)课时作业3
    《大道至简》 第二章 读后感
    NABCD需求
    第九周总结
    第八周总结
    最大子数组(循环连接)
    第七周总结
    最大子数组和
  • 原文地址:https://www.cnblogs.com/lidaojian/p/2492678.html
Copyright © 2011-2022 走看看