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

来源:互联网 发布:ubuntu 32 编辑:程序博客网 时间:2024/05/16 07:18

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

输入样例:                                             对应输出:

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 0
原创粉丝点击