点集Q的凸包是指一个最小凸多边形,满足Q中的点或者在多边形边上或者在其内
如下图所示,点集Q={p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13},则Q的凸包为{p1,p5,p7,p12,p13}
Graham扫描法
原理:从已知的点集Q中先找出一个最左下的点(该点必定在凸包上),然后以该点为起点,将所有点按照与这点的斜率的大小排序,然后依次遍历所有点,如果新加入的点是在原方向上向右拐的,就把前一个点去掉(注意是前一个点,而且这一步是要循环执行的)
当遍历完所有点后,还在栈中的点就是凸包上的点了,依次出栈可以得到从起点开始逆时针旋转的所有凸包上的点
步骤:1.找点 2.排序 3.扫描
#include <iostream>
#include <set>
#include <map>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <bitset>
#include <string>
#include <vector>
#include <iomanip>
#include <cstring>
#include <algorithm>
#include <functional>
#define inf 0x3f3f3f3f
#define debug(x) cout<<"---"<<x<<"---"<<endl
typedef long long ll;
using namespace std;
const double eps = 1e-8;
const double PI = acos(-1.0);
int sgn(double x)
{
if (fabs(x) < eps)
{
return 0;
}
if (x < 0)
{
return -1;
}
else
{
return 1;
}
}
struct Point
{
double x, y;
Point() {}
Point(double _x, double _y)
{
x = _x;
y = _y;
}
Point operator -(const Point &b)const
{
return Point(x - b.x, y - b.y);
}
//叉积
double operator ^(const Point &b)const
{
return x * b.y - y * b.x;
}
//点积
double operator *(const Point &b)const
{
return x * b.x + y * b.y;
}
//绕原点旋转角度B(弧度值),后x,y的变化(逆时针)
void transXY(double B)
{
double tx = x, ty = y;
x = tx * cos(B) - ty * sin(B);
y = tx * sin(B) + ty * cos(B);
}
};
//*两点间距离
double dist(Point a, Point b)
{
return sqrt((a - b) * (a - b));
}
/*
* 求凸包,Graham算法
* 点的编号0~n-1
* 返回凸包结果Stack[0~top-1]为凸包的编号
*/
const int MAXN = 100010;
Point list[MAXN];
int Stack[MAXN], top;
//相对于list[0]的极角排序
bool _cmp(Point p1, Point p2)
{
double tmp = (p1 - list[0]) ^ (p2 - list[0]);
if (sgn(tmp) > 0)
{
return true;
}
else if (sgn(tmp) == 0 && sgn(dist(p1, list[0]) - dist(p2, list[0])) <= 0)
{
return true;
}
else
{
return false;
}
}
void Graham(int n)
{
Point p0;
int k = 0;
p0 = list[0];
//找最下边的一个点
for (int i = 1; i < n; i++)
{
if ( (p0.y > list[i].y) || (p0.y == list[i].y && p0.x > list[i].x) )
{
p0 = list[i];
k = i;
}
}
swap(list[k], list[0]);
sort(list + 1, list + n, _cmp);
if (n == 1)
{
top = 1;
Stack[0] = 0;
return;
}
if (n == 2)
{
top = 2;
Stack[0] = 0;
Stack[1] = 1;
return ;
}
Stack[0] = 0;
Stack[1] = 1;
top = 2;
for (int i = 2; i < n; i++)
{
while (top > 1 && sgn((list[Stack[top - 1]] - list[Stack[top - 2]]) ^ (list[i] - list[Stack[top - 2]])) <= 0)
{
top--;
}
Stack[top++] = i;
}
}
int main()
{
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> list[i].x >> list[i].y;
}
Graham(n);
cout << top << endl;
for (int i = 0; i < top; i++)
{
cout << list[Stack[i]].x << " " << list[Stack[i]].y << endl;
}
}
return 0;
}