题目链接:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_A
题目给出一个多边形,要求出这个多边形的凸包;
在一个点集D中,按一定顺序选取子集Q
使得Q中所有点顺次连接所构成的封闭凸多边形包住D中所有点
可以形象地理解为:有许多个钉子钉在平面上,用一根牛皮筋把所有点包住
找出凸包的思路是:
先把题目给出的多边形的点集合按x坐标从小到大排序,x相同的按y从小到大排序;(点的<前面已经重载过,符合这个要求)
将排序后的集合遍历,先把2个放入栈u中,如果新加入一个点后,能保证u中的点仍然是凸包的,就加进去,否则就pop出来一个点,直到u成为一个凸包为止;这样就得到了凸包的上半部分;
再把点按照x从大到小排序,x相同的y从大到小排序;
其他的操作同上,由此得到凸包的下半部分。
最后,把上下两部分合并(按照要求,本题是要求逆时针方向输出生成凸包中的点)
坑点:输出的时候以凸多边形最下端最左侧的顶点为起点,按逆时针方向依次输出坐标
//求凸包,时间复杂度nlogn
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn=100005;
int n,tot;//n为点的个数,tot为凸点的个数
struct point
{
double x,y;
};
point p[maxn],CHP[maxn];//CHP为凸包最后所构成的点
bool cmp(point a,point b)//水平排序,按x从大到小排,如果x相同,按y从大到小排序
{
return (a.x<b.x||(a.x==b.x&&a.y<b.y));
}
double xmul(point a,point b,point c)//叉积
{
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
int cmpp(point A, point B) ///x从小到大,y从小到大
{
return (A.y<B.y || (A.y == B.y&&A.x<B.x));
}
void Andrew() ///模板
{
sort(p,p+n,cmp);
tot=0;
for(int i=0; i<n; ++i) //计算下半个凸包
{
while(tot>1&&xmul(CHP[tot-2],CHP[tot-1],p[i])<0)
--tot;
CHP[tot++]=p[i];
}
int k=tot;
for(int i=n-2; i>=0; --i) //计算上半个凸包
{
while(tot>k&&xmul(CHP[tot-2],CHP[tot-1],p[i])<0)
--tot;
CHP[tot++]=p[i];
}
if(n>1)//对于只有一个点的包再单独判断
--tot;
}
point q[maxn];
int main()
{
scanf("%d",&n);
for(int i=0; i<n; ++i)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
}
Andrew();
int index;
sort(p,p+tot,cmp); ///将CHP复制到q数组中
for(int i=0; i<tot; ++i)
{
q[i].x=CHP[i].x;
q[i].y=CHP[i].y;
}
sort(q,q+tot,cmpp); ///将q排序,将左下角的坐标放在q【0】.
for(int i=0;i<tot;i++)
{
if((q[0].x==CHP[i].x)&&(q[0].y==CHP[i].y))
{
index=i; ///找到左下角下标
}
}
printf("%d\n",tot);
for(int i=0; i<tot; ++i)
{
printf("%d %d\n",(int)CHP[(i+index)%tot].x,(int)CHP[(i+index)%tot].y);///将左下角的坐标作为第一个
}
return 0;
}