Are You Safe? Gym - 102219H 凸包问题

题意:给你n个坐标点,围成一个多边形,之后给你m个坐标点,逆时针输出点,询问你该点是否在凸包内(不包含点和边上)。

思路:计算几何,运用Graham逆时针排序输出,然后判断该点是否在内就行。

#include <iostream>
#include <string.h>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
#define ll long long
#define N 100005
#define eps 0.00000001//偏差值1e8
#define pi acos(-1.0)//高精度圆周率
#define fori for(i=0;i<n;i++)
#define fori1 for(i=1;i<=n;i++)
const int maxp = 1010;                    //点的数量
int sgn(double x){                        //判断x是否等于0
    if (fabs(x) < eps) return 0;
    else return x < 0 ? -1 : 1;
}
//定义点及其基本运算
struct Point {
    double x, y;
    Point() {}    
    Point(double x, double y) :x(x), y(y) {}
    Point operator + (Point B) { return Point(x + B.x, y + B.y); }
    Point operator - (Point B) { return Point(x - B.x, y - B.y); }
    bool operator ==(Point B) { return sgn(x - B.x) == 0 && sgn(y - B.y) == 0; }
    bool operator <(Point B)                                                    //比较两个点,用于凸包计算
    {
        return sgn(x - B.x) < 0 || (sgn(x - B.x) == 0 && sgn(y - B.y) < 0);
    }
};
Point s[10005],g[10005],h[10005];
typedef Point Vector;                                                        //定义向量
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; }            //叉积
struct Line {                                                                //线上的两个点
    Point p1, p2;
    Line(){}
    Line(Point p1,Point p2):p1(p1),p2(p2){}
    Line(Point p, double angle)
    {
        p1 = p;
        if (sgn(angle - pi / 2) == 0) { p2 = (p1 + Point(0, 1)); }
        else { p2 = (p1 + Point(1, tan(angle))); }
    }
};                                                                            
double mult(Point a, Point b, Point o) {                            /////计算叉乘 ao 和 bo
    return (a.x - o.x) * (b.y - o.y) >= (b.x - o.x) * (a.y - o.y);
}
int Graham(Point p[], int n, Point res[]) {                                    //逆时针排序,构造凸包形状
    int top = 1;
    sort(p, p + n);
    if (n == 0) return 0;
    res[0] = p[0];
    if (n == 1) return 0;
    res[1] = p[1];
    if (n == 2) return 0;
    res[2] = p[2];
    for (int i = 2; i < n; i++) {
        while (top && (mult(p[i], res[top], res[top - 1])))
            top--;
        res[++top] = p[i];
    }
    int len = top;
    res[++top] = p[n - 2];
    for (int i = n - 3; i >= 0; i--) {
        while (top != len && (mult(p[i], res[top], res[top - 1])))
            top--;
        res[++top] = p[i];
    }
    return top;
}
struct Polygon
{
    int n;                                                //多边形的顶点数
    Point p[maxp];                                        //多边形的点
    Line v[maxp];                                        //多边形的边
};
//点和线段的关系:0为点p不在线段v上;1为点p在线段v上
bool Point_on_seg(Point p, Line v)                                            
{
    return sgn(Cross(p - v.p1, v.p2 - v.p1)) == 0 && sgn(Dot(p - v.p1, p - v.p2)) <= 0;
}
//判断点和任意多边形的关系:3为点上;2为边上;1为内部;0为外部
int Point_in_polygon(Point pt, Point* p, int n)
{
    int i;
    for (i = 0; i < n; i++) {
        if (p[i] == pt)return 3;
    }
    for (i = 0; i < n; i++) 
    {
        Line v = Line(p[i],p[(i + 1) % n]);
    if (Point_on_seg(pt, v))return 2;
    }
    int num = 0;
    for (int i = 0; i < n; i++)
    {
        int j = (i + 1)%n;
        int c = sgn(Cross(pt - p[j], p[i] - p[j]));
        int u = sgn(p[i].y - pt.y);
        int v = sgn(p[j].y - pt.y);
        if (c > 0 && u < 0 && v >= 0)num++;
        if (c < 0 && u >= 0 && v < 0)num--;
    }
    return num != 0;
}
int main()
{
    
    ll book[10005];
    ll i, j, k;
    ll n,t,m;
    ll sum = 0, ret = 0,ret2=0;
    ll ans = 0;
    ll flag = 1;
    ll d;
    cin >> t;
    while (t--)
    {
        
        cin >> n >> m;
        fori
            cin >> s[i].x >> s[i].y;
        n = Graham(s, n, h);
        //cout << n << endl;
        for (i = 0; i < m; i++)
            cin >> g[i].x >> g[i].y;
        if (ret)
            cout << endl;
        cout << "Case " << ++ret << endl;
        fori
            printf("%.0lf %.0lf\n", h[i].x, h[i].y);
        printf("%.0lf %.0lf\n", h[0].x, h[0].y);
        for (i = 0; i < m; i++)
        {
            if (!Point_in_polygon(g[i], h, n))//在外部
                cout << g[i].x << " " << g[i].y << " is safe!" << endl;
            else//在内部
                cout << g[i].x << " " << g[i].y << " is unsafe!" << endl;

        }
    }

    
}

 

posted @ 2020-04-03 18:27  ch_hui  阅读(172)  评论(0编辑  收藏  举报