POJ 1228 Grandpa's Estate
这是个好题目,同时也是个不和谐的题目(不和谐原因是题目出的存在漏洞,数据弱,而且有些条件没给清楚,为了一个SB错误无限WA之后,终于AC)
题意就废了我好长时间,唉……英语不好的鸭梨大……
大意就是爷爷留了块土地给我,然而这块土地是以一些钉子来界定的,题目要做的就是给你一堆钉子的坐标(也就是凸包上部分的点),然后问你能不能唯一确定这块土地。
问了下度娘,这个问题叫做“稳定”凸包问题,那么首先就要了解下“稳定”凸包的性质:(在此感谢XDruid博主)
比方说有4个点:
这4个点可以围成一个凸包,但是原始的凸包可能并不是这个样子的
例如可能是这个样子:
这样我们则称这4个点确定的凸包是”不稳定“的!
那么怎么样才叫稳定呢?
是这个样子:想一想,若像刚才一样,在直线外面加一个点,那么线上的点必定不会属于凸包,要删掉,是吧?
如此以来,问题就很明确了,算法也随之而来了:
我们已经知道了怎么求凸包了(前提),这样一来,只要判断是否有3点或N>=3点共线就可以了~~
表示用的是Graham的变种Andrew……感觉真的很好用:
下面贴代码:
#include <cstdio> #include <algorithm> #include <cmath> using namespace std; const double EPS = 1e-6; struct point{ double x , y; } p[1010] , chn[1010]; bool cmp (point a , point b) { return (a.y < b.y || (a.y == b.y && a.x < b.x)); } double xmult(point p1 , point p2 , point p3) { return ((p1.x - p3.x) * (p2.y - p3.y) - (p1.y - p3.y) * (p2.x - p3.x)); } int andrew(int n) { int len , top = 1; chn[0] = p[0]; chn[1] = p[1]; for (int i = 2 ; i < n ; i ++) { while (top && xmult(p[i] , chn[top] , chn[top - 1]) > EPS) top --; chn[++ top] = p[i]; } len = top; chn[++ top] = p[n - 2]; for (int i = n - 3 ; i >= 0 ; i --) { while (top != len && xmult(p[i] , chn[top] , chn[top - 1]) > EPS) top --; chn[++ top] = p[i]; } return top; } bool judge(int n) //解释请看下面 { for (int i = 1 ; i < n ; i ++) { if ((xmult(chn[i - 1] , chn[i] , chn[i + 1]) != 0) && (xmult(chn[i] , chn[i + 1] , chn[i + 2]) != 0)) return false; } return true; } int main() { int T , n; scanf("%d" , &T); while (T --) { scanf("%d" , &n); for (int i = 0 ; i < n ; i ++) scanf("%lf%lf" , &p[i].x , &p[i].y); if (n < 6) printf("NO\n"); else { sort(p , p + n , cmp); int top = andrew(n); if (judge(top)) printf("YES\n"); else printf("NO\n"); } } return 0; }
写了这个题后,对凸包的理解真是加深了。
同时,在被WA了N此后(主要是因为读入数据要用double。但是题目上说是整点啊!不明白,如果没看到别人的解题报告,估计要一直WA下去)
昨晚在无限WA后找了XxX师兄,开始以为思路错了,经过他的问答,对这个题目的理解更是加深了!
下面说下代码中【judge】函数的判断原因:
理论支持:叉积 = 0 说明共线!
我们用Andrew算法求出凸包后(栈内不删去共线的点)这些点已经是按一定顺序排好了的~
然后之需要对栈内的点进行叉积判断就可以了~~ 只有当向左与向中间扩展都不满足是,才说明当前点不满足!
所以只要有一个不满足,则说明全部不满足了。
做了这个题目,我对之前提出的疑问有了答案。哈哈~~