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

题目链接: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 2007

struct 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);

		// 判断顶点是否共线,如果共线直接输出NO
		if (collinear()) {
			printf("NO\n");
			continue;
		}

		// 求凸包, 凸包上的点保存在 c 数组中
		convexHull();

		// 判断凸包上的点是否能唯一确定一个凸多边形
		// 即判断凸包每条线段上都有除了端点外的点
		bool only = verify();

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

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值