凸包模板(分治 or Graham扫描法)

问题概述:空间上有很多点,现在要用一个凸多边形将所有点全部包住,求哪些点在这个凸多边形上

输入样例:                                             对应输出:

4                                                             0 0

0 0                                                          2 3

1 1                                                          3 0

2 3

3 0


分治法(时间复杂度nlogn):

原理:将一个大问题分成几个结构相同的子问题,再把子问题再分成几个更小的子问题…….然后我们就能用递归的

方法,分别求这些子问题的解,最后把每个子问题的解"组装"成原来大问题的解

步骤:

1、把所有的点都放在二维坐标系里面,那么横坐标最小和最大的两个点P1和Pn一定是凸包上的点,这样点集就被分

成了两部分,即X轴的上面和下面,它们分别叫做上包和下包

2、对上包求距离直线P1Pn最远的点Pmax,对下包一样处理

3、作直线P1Pmax、PnPmax,把直线P1Pmax左侧的点当成是上包,把直线PnPmax右侧的点也当成是上包,对下

包一样处理

4、重复步骤2、3直到上方没有点


#include<stdio.h>
#include<limits.h>
#include<string.h>
typedef struct
{
	int x;
	int y;
	int temp;		/*若这点在凸包上,则temp==1,否则temp==0*/
}Point;
Point s[105];
int n;
void Sechup(int a, int b);
void Sechdown(int a, int b);
int main(void)
{
	int T, i, a, b, ax, bx;
	scanf("%d", &T);
	while(T--)
	{
		memset(s, 0, sizeof(s));
		ax = INT_MAX;
		bx = INT_MIN;
		scanf("%d", &n);
		for(i=1;i<=n;i++)
		{
			scanf("%d%d", &s[i].x, &s[i].y);
			if(s[i].x<ax)
				ax = s[i].x, a = i;
			if(s[i].x>bx)
				bx = s[i].x, b = i;
		}
		s[a].temp = s[b].temp = 1;
		Sechup(a, b);			/*上包递归*/
		Sechdown(a, b);				/*下包递归*/
		for(i=1;i<=n;i++)
		{
			if(s[i].temp==1)
				printf("%d %d\n", s[i].x, s[i].y);
		}
	}
	return 0;
}
/*若向量叉乘为负,说明点在直线下面,否则在直线上面(参照直线方向为从左到右)*/
void Sechup(int a, int b)
{
	int i, max, c;
	max = 0;
	for(i=1;i<=n;i++)
	{
		if((s[b].x-s[a].x)*(s[i].y-s[a].y)-(s[b].y-s[a].y)*(s[i].x-s[a].x)>max)
		{
			max = (s[b].x-s[a].x)*(s[i].y-s[a].y)-(s[b].y-s[a].y)*(s[i].x-s[a].x);
			c = i;		/*用向量叉乘求三点三角形面积,使三角形面积最大的那个(非直线上的)点便是离参照直线最远的点Pmax*/
		}
	}
	if(max!=0)/*参照直线上方有点*/
	{
		s[c].temp = 1;
		Sechup(a, c);
		Sechup(c, b);
	}
}

void Sechdown(int a, int b)
{
	int i, max, c;
	max = 0;
	for(i=1;i<=n;i++)
	{
		if((s[b].x-s[a].x)*(s[i].y-s[a].y)-(s[b].y-s[a].y)*(s[i].x-s[a].x)<max)
		{
			max = (s[b].x-s[a].x)*(s[i].y-s[a].y)-(s[b].y-s[a].y)*(s[i].x-s[a].x);
			c = i;
		}
	}
	if(max!=0)
	{
		s[c].temp = 1;
		Sechdown(a, c);
		Sechdown(c, b);
	}
}



Graham扫描法(时间复杂度nlogn):

步骤: 

1、找出y值最小(若y值同等最小,取x小的那个)的作为原点P1

2、以P1为坐标原点,将所有点按相对于P0的幅角(连接原点与x轴的夹角大小)从小到大排序,若幅角相同则按与原

点的距离从小到大排序,并依次给点标记P2、P3……Pn

3、初始点P1和第二个点P2入队列,设P3是当前点

4、设当前队伍尾端的点A(x1, y1),队伍尾端倒数第二个点B(x2, y2)和当前点C(x3, y3),求向量AB与向量AC的叉

乘,若为正,则说明点A不在凸包上,将点A踢出队列,执行步骤5,否则说明点在凸包上,执行步骤6

5、当前点仍不变,继续执行步骤4

6、当前点入队,将下一个点作为当前点,执行步骤4,直到所有点全部遍历完毕(最后一个点Pn入队)


#include<stdio.h>
#include<limits.h>
#include<stdlib.h>
#include<string.h>
#include<deque>
#include<algorithm>
using namespace std;
typedef struct
{
	int x;
	int y;
}Point;
int n, temp;
Point top1, top2, s[105];
deque<Point> q;
bool comp2(Point a, Point b)	/*按幅角排序*/
{
	if((a.x-top1.x)*(b.y-top1.y)-(a.y-top1.y)*(b.x-top1.x)>0)
		return 1;
	else if((a.x-top1.x)*(b.y-top1.y)-(a.y-top1.y)*(b.x-top1.x)==0 && abs(a.x-top1.x)<abs(b.x-top1.x))
		return 1;
	return 0;
}
int main(void)
{
	int T, i, ax, ay;
	scanf("%d", &T);
	while(T--)
	{
		ax = ay = INT_MAX;
		scanf("%d", &n);
		for(i=1;i<=n;i++)
		{
			scanf("%d%d", &s[i].x, &s[i].y);
			if(s[i].y<ay || s[i].y==ay && s[i].x<ax)
				temp = i, ay = s[i].y, ax = s[i].x;		/*步骤1*/
		}
		top1 = s[temp];
		sort(s+1, s+n+1, comp2);
		q.push_back(s[1]);
		q.push_back(s[2]);
		temp = 3;		/*步骤3*/
		while(temp<=n)
		{
			top1 = q.back();
			q.pop_back();		/*临时弹出用作判定*/
			top2 = q.back();
			if((top2.x-top1.x)*(s[temp].y-top1.y)-(top2.y-top1.y)*(s[temp].x-top1.x)<=0)		/*步骤4*/
			{
				q.push_back(top1);		/*符合条件,临时弹出的归队*/
				q.push_back(s[temp]);	/*当前点入队*/
				temp++;			/*下一个点作为当前点*/
			}
			/*不符合条件*/
		}
		while(q.empty()==0)
		{
			top1 = q.back();
			printf("%d %d\n", top1.x, top1.y);
			q.pop_back();
		}
	}
	return 0;
}


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值