POJ 1228 Grandpa's Estate 计算凸包+判断点在线段上

来源:互联网 发布:医院网络通知管理 编辑:程序博客网 时间:2024/04/28 21:30

题目链接:http://poj.org/problem?id=1228

题目大意:给定n个二维坐标系中的点,这些点是一个凸多边形顶点的子集。判断这些点是否能唯一确定原来的凸多边形。

解题思路:
考虑多边形的一条边。对于给定两个端点,连起来变成一条边。原来的多边形中可能在这条边之外还有一个端点p',那么这条边在原多边形中可能就不存在。 那么怎么能确定一条边一定在原多边形中存在呢?

如果在一条线段上除了端点p1 p2之外有其他的点p3,那么这条边在原多边形中一定存在。因为不可能添加一个顶点使得这条线段仍然是凸的。p3限制了线段p1p2向外凸出。所以只需要先对点集求个凸包,然后判断凸包的每条边上是否都有除了端点之外的第三个点,使得这条边确定下来。如果凸包中的每条边都满足这个条件,便可唯一确定原来的凸多边形。

另外这道题也有需要特判的情况,以上的分析都基于能求出一个多边形凸包,实际上这样的凸包可能并不存在。我使用的是xy坐标排序的graham-scan算法,如果所有的点都共线的话,算法依然会求出一个点集(包括了输入的所有的点),并且可能判断出点集中每条的线段上都有第三个顶点,然后输出YES。实际上很显然,直线外可能存在其他的顶点把这条线“撑开”,答案应该是NO。所以必须特判所有顶点共线的情况。按照惯例,考虑n的极限情况,n=1时,只有一个顶点。这时候判断共线的函数会把这种情况判断成共线,所以n=1时也没有问题。

至于复杂度: 求凸包O(nlogn),判断唯一性时枚举每条边和每个点,O(nh),其中h是凸包中点的数目。所以总复杂度在O(nlogn)和O(nh)之间。

/* *  2014.11.8 *  Problem: 1228 *  Memory: 204K *  Time: 16MS */#include "stdio.h"#include "math.h"#define EPS 1e-8#define MAXN 2007struct Point {double x, y;}p[MAXN], c[MAXN];int n, top;void sort(int l, int r) {int i=l, j=r;double x = p[(l+r)>>1].x, y = p[(l+r)>>1].y;do {while (p[i].x<x || (p[i].x==x && p[i].y<y)) i++;while (p[j].x>x || (p[j].x==x && p[j].y>y)) j--;if (i<=j) {Point t = p[i]; p[i] = p[j]; p[j] = t;i++; j--;}} while (i<=j);if (i<r) sort(i, r);if (l<j) sort(l, j);}double max(double a, double b) {return a>b?a:b;}double min(double a, double b) {return a>b?b:a;}// 叉积double cross(Point a, Point b, Point c) {double x1 = b.x-a.x, y1 = b.y-a.y;double x2 = c.x-a.x, y2 = c.y-a.y;return x1*y2-x2*y1;}// 判断是否左转, 包括共线情况bool turnLeft(Point a, Point b, Point c) {return cross(a, b, c)>=-EPS;}// 求凸包void convexHull() {sort(1, n);top = 1; c[top] = p[1];for (int i=2;i<=n;i++) {while (top>=2 && turnLeft(c[top-1], c[top], p[i])) top--;c[++top] = p[i];}int pn = top;for (int i=n-1;i>=1;i--) {while (top>=pn+1 && turnLeft(c[top-1], c[top], p[i])) top--;c[++top] = p[i];}top --;}// 判断点是否在线段上,不包括端点bool onSegment(Point a, Point b, Point c) {if (fabs(cross(a, b, c))<EPS &&     min(a.x,b.x)<=c.x && c.x<=max(a.x,b.x) &&    min(a.y,b.y)<=c.y && c.y<=max(a.y,b.y) &&!((c.x==a.x&&c.y==a.y)||(c.x==b.x&&c.y==b.y)))    return true;return false;}// 验证凸包中的每一条边是否确定bool verify() {c[top+1] = c[1];for (int i=1;i<=top;i++) {bool on = false;for (int j=1;j<=n;j++)if (onSegment(c[i], c[i+1], p[j])) {on = true;break;}if (!on) return false; }return true;}// 判断所有点是否共线bool collinear() {p[n+1] = p[1];p[n+2] = p[2];for (int i=1;i<=n;i++) if (fabs(cross(p[i],p[i+1],p[i+2]))>EPS) return false;return true;}int main() {int t;scanf("%d", &t);while (t--) {scanf("%d", &n);for (int i=1;i<=n;i++) scanf("%lf %lf", &p[i].x, &p[i].y);// 判断顶点是否共线,如果共线直接输出NOif (collinear()) {printf("NO\n");continue;}// 求凸包, 凸包上的点保存在 c 数组中convexHull();// 判断凸包上的点是否能唯一确定一个凸多边形// 即判断凸包每条线段上都有除了端点外的点bool only = verify();if (only) {printf("YES\n");} else {printf("NO\n");}}return 0;}

0 0
原创粉丝点击