POJ 1228 Grandpa's Estate【稳定凸包判断】

76 篇文章 0 订阅
24 篇文章 0 订阅

链接:


Grandpa's Estate
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 9642 Accepted: 2606

Description

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

Source



算法:计算几何 凸包 叉积



题意:


表示没看懂题目,去别人那里看的。。。
给你 N 个点的坐标,求凸包,判断凸包是否是稳定凸包。
稳定凸包:凸包的每条边至少有三个点



思路:

主体思路:先求 凸包 ,求凸包的过程中注意下不要像以往一样去掉共线的点。
                    然后再把凸包的每条边遍历一遍,看是否每条边都满足至少含有三个点。

嗯,然后就 WA 了。

注意:多边形至少有三 条边,每条边上至少要有三个点,那么 N < 6 肯定是可以直接输出 NO 的
           同理:计算出凸包后,如果凸包上的点的个数 M < 6, 那么还是直接输出 NO 就可以了。

          WA
          继续看。。。
          有可能求出的凸包上的点全都在同一条直线上有木有,叉积遍历一遍,直接 NO

          WA。。。
          那么就有可能遍历凸包的边的时候没有处理好,忽略了最后一条边只有两个点的情况。
          那么在遍历完凸包后,再单独处理一下最后三个点就可以了。

最后:用下面的数据测试一下,过了基本就没问题了。。。


很裸的一题了,过的人2000+,但是自己写的时候还是出了点问题,最后还是看了讨论区的数据才过的。
感谢讨论区的数据:【当然这样的习惯很不好Orz】

Posted by proverbs at 2013-02-24 10:28:09 on Problem 1228
Input:
4
6
1  1  2  2  3  3  4  4  5  5  6  6
1
1 1
8
0  0  0  1  0  2  0  3  1  1  2  2  3  3  2  3
8
0  0  0  2  1  0  1  2  2  2  3  0  3  1  3   2
Output:
NO
NO
YES
NO



PS:表示计算几何最好总结自己的模板,知道思路后,基本上就是码字了。不推荐看别人的代码。。。麻烦也不利于提高。
        这里写清楚点,主要是方便以后自己复习。如果会复习的话。。。


code:

1228Accepted756K0MSG++2662B2013-08-22 20:32:28

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<iostream>
using namespace std;

const int maxn = 1000+10;
int n,m;

struct Point {
    double x,y;
    Point() {}
    Point(double _x, double _y) { x = _x; y = _y; }

    Point operator - (const Point &b) const {
        return Point(x-b.x, y-b.y);
    }
}p[maxn], ch[maxn];

//高精度判断
const double eps = 1e-5;
int dcmp(double x) {
    if(x < eps) return 0;
    else return x < 0 ? -1 : 1;
}
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;
}

bool cmp(Point a, Point b) {
    return (a.x == b.x && a.y < b.y) || a.x < b.x;
}

//为判定是否为稳定凸包, 求凸包时保存共线的点,叉积时去掉 =
void ConvexHull(Point *p, int n, Point *ch) {
    sort(p, p+n, cmp);

    m = 0;
    for(int i = 0; i < n; i++) {
        while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) < 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-2]) < 0) m--;
        ch[m++] = p[i];
    }

    if(n > 1) m--;
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        double x,y;
        for(int i = 0; i < n; i++) {
            scanf("%lf%lf", &x, &y);
            p[i] = Point(x, y);
        }

        if(n < 6) { //如果开始的点少于 6 个肯定不行
            printf("NO\n"); continue;
        }

        ConvexHull(p, n, ch);
        if(m < 6) { //如果凸包的点少于 6 个肯定不行
            printf("NO\n"); continue;
        }

//排除凸包上的所有的点共线的情况
//注意:下面的点用凸包上的点ch[], 不小心写错成了 p[] 贡献了两次 WA
        int flag = 1;
        for(int i = 0; i < m-1; i++) {
            double c = Cross(ch[i+1]-ch[i], ch[(i+2)%m]-ch[i+1]);
            if(dcmp(c) != 0) { //说明现有的点不共线, 暂且合法
                flag = 0; break;
            }
        }
        if(flag == 1) {
            printf("NO\n"); continue;
        }

        //判断凸包是否稳定:即每条边都至少有三个点
        flag = 1;
        ch[m] = ch[0];
        for(int i = 0; i < m-1;) {
//printf("test i = %d\n", i); //此处的 i 是凸包每条边的第一个点, 可能不包含最后一条边。。。
            double c = Cross(ch[i+1]-ch[i], ch[(i+2)%m]-ch[i+1]);
            if(dcmp(c != 0)) { //printf("erro: i =%d, c = %.0lf\n", i, c);
                flag = 0; break;
            }

            while(c == 0 && i < m-1) { //走到当前边的尽头
                i++;
                c = Cross(ch[i+1]-ch[i], ch[(i+2)%m]-ch[i+1]);
            } i++; //下一条边起点的编号
        }
        if(flag) { //判断最后一条边是否固定。。。
            double c = Cross(ch[m]-ch[m-1], ch[m-1]-ch[m-2]);
            if(c != 0) flag = 0;
        }

        if(flag) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值