1 条题解

  • 0
    @ 2025-5-5 15:44:09

    说明

    该代码解决的问题是:给定一组平面上的点(钉子),判断这些点是否能够唯一确定一个凸多边形的边界。换句话说,判断这些点是否恰好构成一个凸多边形的顶点,且没有其他点位于凸多边形的边上。

    算法标签

    • 计算几何
    • 凸包算法
    • 点与线段关系

    解题思路

    1. 输入处理:读取测试用例的数量 t,然后逐个处理每个测试用例。每个测试用例包含点的数量 n 和每个点的坐标。
    2. 计算凸包:使用 Andrew's monotone chain 算法计算给定点集的凸包,得到凸包的顶点序列 ch 和顶点数量 m
    3. 特殊情况处理:如果凸包的顶点数量 m 为1或2(即点集退化为点或线段),则直接输出 "NO",因为这些情况无法唯一确定一个凸多边形。
    4. 检查点是否在凸包边上:对于凸包的每条边,检查是否有点(非顶点)位于该边上。如果有,则说明无法唯一确定凸多边形边界,输出 "NO";否则输出 "YES"。

    分析

    • 凸包计算:使用 Andrew's monotone chain 算法高效计算凸包,时间复杂度为 O(n log n)
    • 点与线段关系:通过叉积和点积判断点是否在线段上(不包含端点),确保严格位于线段内部。
    • 唯一性判断:只有当所有点要么是凸包的顶点,要么严格位于凸包内部时,才能唯一确定凸多边形边界。如果有任何点位于凸包的边上(非顶点),则无法唯一确定边界。

    实现步骤

    1. 读取输入:读取测试用例的数量 t,然后逐个处理每个测试用例。
    2. 计算凸包:对输入的点集进行排序,然后计算凸包的顶点序列 ch 和顶点数量 m
    3. 特殊情况处理:如果 m 为1或2,直接输出 "NO"。
    4. 检查边上的点:对于凸包的每条边,检查是否有点(非顶点)位于该边上。如果有,输出 "NO";否则输出 "YES"。

    代码

    #include <cstring>
    #include <cmath>
    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    using namespace std;
    typedef unsigned long long ll;
    #define maxn 1111
    #define pi acos (-1)
    #define rotate Rotate
     
    const double eps = 1e-8;
    int dcmp (double x) {
        if (fabs (x) < eps)
            return 0;
        else return x < 0 ? -1 : 1;
    }
    struct point {
        double x, y;
        point (double _x = 0, double _y = 0) : x(_x), y(_y) {}
        point operator - (point a) const {
            return point (x-a.x, y-a.y);
        }
        point operator + (point a) const {
            return point (x+a.x, y+a.y);
        }
        bool operator < (const point &a) const {
            return x < a.x || (x == a.x && y < a.y);
        }
        bool operator == (const point &a) const {
            return dcmp (x-a.x) == 0 && dcmp (y-a.y) == 0;
        }
    };
     
    point operator * (point a, double p) {
        return point (a.x*p, a.y*p);
    }
    double cross (point a, point b) {
        return a.x*b.y-a.y*b.x;
    }
    double dot (point a, point b) {
        return a.x*b.x + a.y*b.y;
    }
     
    int n, m, tot;
    point p[maxn], ch[maxn];
     
    int ConvexHull () {
        sort (p, p+n);
        int m = 0;
        for (int i = 0; i < n; i++) {
            while (m > 1 && cross (ch[m-1]-ch[m-2], p[i]-ch[m-1]) <= 0)
                m--;
            ch[m++] = p[i];
        }
        int k = m;
        for (int i = n-2; i >= 0; i--) {
            while (m > k && cross (ch[m-1]-ch[m-2], p[i]-ch[m-1]) <= 0)
                m--;
            ch[m++] = p[i];
        }
        if (n > 1)
            m--;
        return m;
    }
     
    bool PointOnSegment (point p, point a1, point a2) { //点在线段上(不包含端点)
        return dcmp (cross (a1-p, a2-p)) == 0 && dcmp (dot (a1-p, a2-p)) < 0;
    }
     
    void solve () {
        if (m == 1 || m == 2) {
            printf ("NO\n");
            return ;
        }
        bool vis[maxn];
        memset (vis, 0, sizeof vis);
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (PointOnSegment (p[j], ch[i], ch[(i+1)%m]))
                    vis[i] = 1;
            }
        }
        for (int i = 0; i < m; i++) {
            if (!vis[i]) {
                printf ("NO\n");
                return ;
            }
        }
        printf ("YES\n");
        return ;
    }
     
    int main () {
        //freopen ("in", "r", stdin);
        int t;
        cin >> t;
        while (t--) {
            cin >> n;
            for (int i = 0; i < n; i++) {
                cin >> p[i].x >> p[i].y;
            }
            m = ConvexHull ();
            solve ();
        }
        return 0;
    }
    • 1

    信息

    ID
    229
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者