您的位置:首页 > 其它

POJ 1228 Grandpa's Estate (凸包、保留凸包边上的点)

2016-09-03 09:34 453 查看
题目链接:http://poj.org/problem?id=1228

题意:输入一个凸包上的点(没有凸包内部的点,要么是凸包顶点,要么是凸包边上的点),判断这个凸包是否稳定。所谓稳定就是判断能不能在原有凸包上加点,得到一个更大的凸包,并且这个凸包包含原有凸包上的所有点。

参考博客:http://blog.csdn.net/acdreamers/article/details/10023615

当一个凸包稳定时,凸包的每条边上都要有至少3个点,若只有两个点,则一定可以增加一个点,得到更大的凸包。这样我们可以求出凸包,在求凸包时把共线的点也加进来,这样我们就判断是否有连续的三点共线即可,具体参见代码。

搞了半天也没想明白怎样用Graham算法来保存所有凸包边上共线的点。不过网上竟然有人这样过了.....

一晚上没搞出来,第二天换了种比较蠢的办法,就是先求出凸包顶点,然后对每条边枚举所有点看是否每条边全都是有三个以上的点。这样也可以过,不过还要特判所有点共线的情况,这种情况下凸包Stack里面就只有2个点了,很容易判断。  下面是代码:

附带上一个第二天下午写的,通过Andrew算法保留边上点的做法。网上很多代码,道理讲得很明白,可代码明显有错,有的连所有点共线的情况都没考虑,虽然能过.....

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
const double eps = 1e-8;
const double PI = acos(-1.0);
int sgn(double x) {
if(fabs(x) < eps)return 0;
if(x < 0)return -1;
else return 1;
}
struct Point {
double x,y, v, l;
Point(){}
Point(double _x,double _y) {
x = _x;y = _y;
}
Point operator -(const Point &b)const {
return Point(x - b.x,y - b.y);
}
double operator ^(const Point &b)const {
return x*b.y - y*b.x;
}
double operator *(const Point &b)const {
return x*b.x + y*b.y;
}
void transXY(double B) {
double tx = x,ty = y;
x = tx*cos(B) - ty*sin(B);
y = tx*sin(B) + ty*cos(B);
}
};

struct Line {
Point s,e;
Line(){}
Line(Point _s,Point _e)  {
s = _s;e = _e;
}
pair<int,Point> operator &(const Line &b)const {
Point res = s;
if(sgn((s-e)^(b.s-b.e)) == 0) {
if(sgn((s-b.e)^(b.s-b.e)) == 0) return make_pair(0,res);
else return make_pair(1,res);
}
double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
res.x += (e.x-s.x)*t;
res.y += (e.y-s.y)*t;
return make_pair(2,res);
}
};

double dist(Point a,Point b) {
return sqrt((a-b)*(a-b));
}

const int MAXN = 1010;
Point list[MAXN];
int Stack[MAXN], top;
bool _cmp(Point p1,Point p2) {
double tmp = (p1-list[0])^(p2-list[0]);
if(sgn(tmp) > 0)return true;
else if(sgn(tmp) == 0 && sgn(dist(p1,list[0]) - dist(p2,list[0])) <= 0)
return true;
else return false;
}
void Graham(int n) {
Point p0;
int k = 0;
p0 = list[0];
for(int i = 1;i < n;i++)  {
if( (p0.y > list[i].y) || (p0.y == list[i].y && p0.x > list[i].x) )   {
p0 = list[i];    k = i;
}
}
swap(list[k],list[0]);
sort(list+1,list+n,_cmp);

if(n == 1)  {
top = 1;
Stack[0] = 0;
return;
}
if(n == 2)  {
top = 2;
Stack[0] = 0;
Stack[1] = 1;
return ;
}
Stack[0] = 0;
Stack[1] = 1;
top = 2;
for(int i = 2;i < n;i++)  {
while(top > 1 && sgn((list[Stack[top-1]]-list[Stack[top-2]])^(list[i]-list[Stack[top-2]])) <= 0){
top--;
}
Stack[top++] = i;
}
}

int main() {
int t;
scanf("%d", &t);
while(t--) {
int n;
scanf("%d", &n);
int i, j;
for(i = 0; i < n; i++) {
scanf("%lf %lf", &list[i].x, &list[i].y);
}
if(n < 6) {  //特判
puts("NO");
continue;
}
Graham(n);
if(top <= 2) {  //所有点共线
puts("NO");
continue;
}
int flag = 1;
for(i = 0; i < top; i++) {
Line l1(list[Stack[i]], list[Stack[(i + 1) % top]]);
for(j = 0; j < n; j++) {
if(j == Stack[i] || j == Stack[(i + 1) % top]) continue;
Line l2(list[Stack[i]], list[j]);
pair<int ,Point> p = (l1 & l2);
if(p.first == 0) {
break;
}
}
if(j == n) { //有一条边无共线点
flag = 0;
break;
}
}
puts(flag ? "YES" : "NO");
}
return 0;
}


再研究一下怎样求凸包时保存所有边上的顶点....

Andrew算法:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
const double eps = 1e-8;
const double PI = acos(-1.0);
int sgn(double x) {
if(fabs(x) < eps)return 0;
if(x < 0)return -1;
else return 1;
}
struct Point {
double x,y, v, l;
Point(){}
Point(double _x,double _y) {
x = _x;y = _y;
}
Point operator -(const Point &b)const {
return Point(x - b.x,y - b.y);
}
double operator ^(const Point &b)const {
return x*b.y - y*b.x;
}
double operator *(const Point &b)const {
return x*b.x + y*b.y;
}
void transXY(double B) {
double tx = x,ty = y;
x = tx*cos(B) - ty*sin(B);
y = tx*sin(B) + ty*cos(B);
}
};

struct Line {
Point s,e;
Line(){}
Line(Point _s,Point _e)  {
s = _s;e = _e;
}
pair<int,Point> operator &(const Line &b)const {
Point res = s;
if(sgn((s-e)^(b.s-b.e)) == 0) {
if(sgn((s-b.e)^(b.s-b.e)) == 0) return make_pair(0,res);
else return make_pair(1,res);
}
double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
res.x += (e.x-s.x)*t;
res.y += (e.y-s.y)*t;
return make_pair(2,res);
}
};

double dist(Point a,Point b) {
return sqrt((a-b)*(a-b));
}

///计算凸包,输入点数组p,个数为n,输出点数组ch。函数返回凸包顶点数。
///输入不能有重复点,函数执行完成之后输入点的顺序被破坏。
///如果不希望凸包的边上有输入点,把两个 < 改成 <=
///在精度要求高时,应该使用dcmp函数比较
const int MAXN = 1010;
Point list[MAXN];
Point Stack[MAXN];
int top;
bool _cmp(Point p1,Point p2) {
if(p1.x != p2.x) return p1.x < p2.x;
else return p1.y < p2.y;

}
int Andrew(Point *p, int n, Point *ch) {  //不加=号,逆时针顺序保存所有点
sort(p, p + n, _cmp);
int m = 0;
for(int i = 0; i < n; i++) {
while(m > 1 && ((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 && ((ch[m - 1] - ch[m - 2]) ^ (p[i] - ch[m - 2])) < 0) m--;
ch[m++] = p[i];
}
if(n > 1) m--;
return m;
}

int main() {
int t;
scanf("%d", &t);
while(t--) {
int n;
scanf("%d", &n);
int i, j;
for(i = 0; i < n; i++) {
scanf("%lf %lf", &list[i].x, &list[i].y);
}
if(n < 6) {  //特判
puts("NO");
continue;
}
top = Andrew(list, n, Stack);
for(i = 0; i < top - 2; i++) {    //判断是否所有点都共线
Line l1(Stack[i], Stack[i + 1]);
Line l2(Stack[i + 1], Stack[i + 2]);
if((l1 & l2).first != 0) {
break;
}
}
if(i == top - 2) {
puts("NO");
continue;
}
int flag = 1;
for(i = 0; i < top; i++) {  //每两个点为一组,判断是否每条边上至少三个点
Line l1(Stack[((i - 1) + top) % top], Stack[i]);
Line l2(Stack[((i - 1) + top) % top], Stack[(i + 1) % top]);
Line l3(Stack[i], Stack[(i + 1) % top]);
Line l4(Stack[i], Stack[(i + 2) % top]);
pair<int, Point> p1 = (l1 & l2);
pair<int, Point> p2 = (l3 & l4);
if(p1.first != 0 && p2.first != 0)  {
flag = 0;
break;
}
}
puts(flag ? "YES" : "NO");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: