Grandpa's Estate (凸包)

传送门

Being the only living descendant of his grandfather, Kamran the Believer inherited all of the grandpa's belongings. The most valuable one was a piece of convex polygon shaped farm in the grandpa's birth village. The farm was originally separated from the neighboring farms by a thick rope hooked to some spikes (big nails) placed on the boundary of the polygon. But, when Kamran went to visit his farm, he noticed that the rope and some spikes are missing. Your task is to write a program to help Kamran decide whether the boundary of his farm can be exactly determined only by the remaining spikes.

Input

The first line of the input file contains a single integer t (1 <= t <= 10), the number of test cases, followed by the input data for each test case. The first line of each test case contains an integer n (1 <= n <= 1000) which is the number of remaining spikes. Next, there are n lines, one line per spike, each containing a pair of integers which are x and y coordinates of the spike.

Output

There should be one output line per test case containing YES or NO depending on whether the boundary of the farm can be uniquely determined from the input.

Sample Input

1
6 
0 0
1 2
3 4
2 0
2 4 
5 0

Sample Output

NO

题意:给你一个凸多变形,判断它是否稳定的凸包

稳定凸包:

比如有4个点:

 

这四个点是某个凸包上的部分点,他们连起来后确实还是一个凸包。
但是原始的凸包可能不是这样。比如:

 

即这四个点构成的凸包不算做“稳定”的。我们发现,当凸包上存在一条边上的点只有端点两个点的时候,这个凸包不是稳定的,因为它可以在这条边外再引入一个点,构成一个新的凸包。但一旦一条边上存在三个点,那么不可能再找到一个点使它扩展成一个新的凸包,否则构成的新多边形将是凹的。
下面是一个典型的稳定凸包:

 

判断是否稳定凸包的方法:
求出给定这堆点的新的凸包,然后判断凸包上的每条边上是否至少有3个点存在,假如有一条边不符合条件,那么就不是稳定凸包。

(关于稳定凸包的解释转自https://www.jianshu.com/p/1d056ac1c881

题解:就是判断该凸变形的每一条边上是否含有三个以上所给出的点

给一组简单样例:

1
9
0 0
2 0
4 0
4 2
4 4
1 1
2 2
2 1
3 2

YES

附上AC代码:

//#include"bits/stdc++.h"
//#include<unordered_map>
//#include<unordered_set>
#include<iostream>
//#include<sstream>
#include<iterator>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<vector>
#include<bitset>
#include<climits>
#include<queue>
#include<iomanip>
#include<cmath>
#include<stack>
#include<map>
#include<ctime>
#include<new>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define MT(a,b) memset(a,b,sizeof(a))
#define lson l, mid, node<<1
#define rson mid + 1, r, node<<1|1
const int INF  =  0x3f3f3f3f;
const int O    =  1e6;
const int mod  =  10007;
const int maxn =  2e3 + 5;
const double PI  =  acos(-1.0);
const double E   =  2.718281828459;
const double esp = 1e-7;

struct Point { double x, y; };

// 向量基本模板
typedef Point Vector;

//Vector operator + (Vector a, Vector b) { return { a.x + b.x, a.y + b.y }; }
Vector operator - (Vector a, Vector b) { return { a.x - b.x, a.y - b.y }; }
//Vector operator * (Vector a, double p) { return { a.x * p, a.y * p}; }
//Vector operator / (Vector a, double p) { return { a.x / p, a.y / p}; }
//
//bool operator < (Vector a, Vector b) { return a.x * a.x + a.y * a.y < b.x * b.x + b.y * b.y; }
//bool operator == (Vector a, Vector b) { return fabs(a.x - b.x) < esp && fabs(a.y - b.y) < esp; }

// 点积,模长,角度,叉积,三角形面积,逆时针旋转
//double Dot(Vector a, Vector b) { return a.x * b.x + a.y * b.y; }
//double Length(Vector a) { return sqrt( Dot(a, a) ); }
//double Angle(Vector a, Vector b) { return acos(Dot(a, b) / Length(a) / Length(b)); }
double Cross(Vector a, Vector b) { return a.x * b.y - a.y * b.x; } // 大于0时,b在a的逆时针方向
double Area(Point A, Point B, Point C) { return Cross(B - A, C - A) / 2; } // 三角形面积叉乘公式
//Vector Rotate (Vector a, double rad) { return { a.x*cos(rad) - a.y*sin(rad), a.x*sin(rad) + a.y*cos(rad) }; }
//int dcmp(double x) { if(fabs(x) < esp) return 0; else return x < 0 ? -1 : 1; } // 精准判断正负号


// 凸包模板
bool com(Point A, Point B){ return (fabs(A.x - B.x) < esp && A.y < B.y) || A.x < B.x; }
int ConvexHull(Point *P, int n, Point *A){ // P为点集合, A为凸包点(包括边上的点)
    sort(P, P + n, com);
    int m = 0;
    for(int i=0; i<n; i++) {
        while(m > 1 && Cross(A[m-1] - A[m-2], P[i] - A[m-2]) < 0) m --;
        A[m++] = P[i];
    }
    int k = m;
    for(int i=n-2; i>=0; i--){
        while(m > k && Cross(A[m-1] - A[m-2], P[i] - A[m-2]) < 0) m --;
        A[m++] = P[i];
    }
    if(n > 1) m -- ;
    return m;
}

int main(){
    int T; scanf("%d", &T);
    while( T --) {
        int n; scanf("%d", &n);
        Point P[maxn]; for(int i=0; i<n; i++) scanf("%lf%lf", &P[i].x, &P[i].y);
        Point A[maxn]; int k = ConvexHull(P, n, A); // A 为凸包点
        bool flag = true;
        for(int i=0; i<k; i++) {
            Point p1 = A[(i+k-1) % k];
            Point p2 = A[i];
            Point p3 = A[(i+k+1) % k];
            Point p4 = A[(i+k+2) % k];
            if(Area(p1, p2, p3) != 0) // 判断p2是否顶点
                if(Area(p2, p3, p4) != 0) flag = false;  // 如果p2是顶点判断p2,p3,p4是否共线
        }
        if(k < 6) flag = false; // 形成稳定凸包至少存在6个点
        printf(flag ?  "YES\n":"NO\n");
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值