凸包问题

根据极角排序Graham扫描法求凸包(但无法完全保留凸包边上的点)

1. 找出最左下的点(首先要最下,有多个最下则找最左),记为p[0]

2. 以p[0]为原点,对p[1]...p[n-1]进行按极角排序。极角相同的,距离p[0]近的算小
3. 栈里面放入p[0],p[1],p[2]

for(int i = 3; i < n; ++i) {

while(true) {

    考察栈顶元素 k2,k2下方元素k1,以及p[i]
考虑 k1->k2->p[i]
if( 在k2这一点直走或者向右拐了) {
将k2出栈
else break;
}
p[i]入栈
}

4. 栈中的点就是凸包的所有顶点

//极角序 Graham扫描法
struct Point{
	double x,y;
	Point(double x,double y) :x(x),y(y){}
	Vector operator - (const Point& p){
		return Vector(x-p.x,y-p.y);
	}
	bool operator < (const Point& p) const {  //注意是取下左点,最下中的最左 
		if(p.y==y) return x<p.x;
		return y<p.y;
	}
};

vector<Point> points;
vector<Point> stack;

double cross(Vector a,Vector b){
	return a.x*b.y-a.y*b.x;
}
int Sign(double x){ 
	if(fabs(x)<EPS) return 0;
	return x<0?-1:1;
}
double dist(const Point& a,const Point& b){
	return hypot(fabs(a.x-b.x),fabs(a.y-b.y));
}
struct cmp{
	Point p0;
	cmp(Point p):p0(p){}
	bool operator () (Point p1,Point p2) const {
		int s=Sign(cross(p1-p0,p2-p0));
		if(s>0) return true;
		else if(s<0) return false;
		else {
			if(dist(p1,p0)<dist(p0,p2)) return true;
			else return false;
		}
	}
};

int Graham()
{
	if(points.size()<3) return 0;
	stack.clear();
	
	sort(points.begin(),points.end()); //找出下左的点 
	sort(points.begin()+1,points.end(),cmp(points[0]));
	
	stack.push_back(points[0]); 
	stack.push_back(points[1]); 
	stack.push_back(points[2]);
	for(int i=3;i<points.size();i++)
	{
		for(;;){
			Point p2=*(stack.end()-1);
			Point p1=*(stack.end()-2);
			if(Sign(cross(p2-p1,points[i]-p2))<=0) //小于零代表向右拐或直走 
				stack.pop_back();
			else break;
		}
		stack.push_back(points[i]); //i点肯定是向右拐的 
	}
}

根据水平排序Graham扫描法求凸包(可以求出凸包边上的点,就是比极角序的麻烦)

判断的方法是否是凸包的方法和极角的一样,就是求一边只求出了一半的凸包,还要倒着扫一遍点

//水平序Graham扫描法求凸包
struct Point{
	double x,y;
	Point(double x,double y) :x(x),y(y){}
	Vector operator - (const Point& p){
		return Vector(x-p.x,y-p.y);
	}
	bool operator < (const Point& p) const {  //注意取下左点 
		if(p.y==y) return x<p.x;
		return y<p.y;
	}
};

vector<Point> points;
vector<Point> stack;

double cross(Vector a,Vector b){
	return a.x*b.y-a.y*b.x;
}
int Sign(double x){ 
	if(fabs(x)<EPS) return 0;
	return x<0?-1:1;
}
double dist(const Point& a,const Point& b){
	return hypot(fabs(a.x-b.x),fabs(a.y-b.y));
}
int Graham()
{
	if(points.size()<3) return 0;
	stack.clear();
	
	sort(points.begin(),points.end());
	stack.push_back(points[0]);
	stack.push_back(points[1]);
	
	int n=points.size(); 
	for(int i=2;i<n;i++)
	{
		while(stack.size()>1){  //一定要这一条,因为第二个点不一定是凸包可能会pop,而极角排序的肯定是凸包中的点 
			Point p2=*(stack.end()-1);
			Point p1=*(stack.end()-2);
			if(Sign(cross(p2-p1,points[i]-p2))<0) //不加等于,可以保留凸包上的点 
				stack.pop_back();
			else break;
		}
		stack.push_back(points[i]);
	} 
	
	int size=stack.size();
	stack.push_back(points[n-2]); //因为此时栈顶元素是points[n-1],下一个就是n-2
	for(int i=n-3;i>=0;i--)
	{
		while(stack.size()>size){
			Point p2=*(stack.end()-1);
			Point p1=*(stack.end()-2);
			if(Sign(cross(p2-p1,points[i]-p2))<0)
				stack.pop_back();
			else break;
		}
		stack.push_back(points[i]);
	}
	stack.pop_back(); //points[0]被重复push两次了 
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值