问题概述:空间上有很多点,现在要用一个凸多边形将所有点全部包住,求哪些点在这个凸多边形上
输入样例: 对应输出:
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;
}