基础
点
const double eps = 1e-8;
int sgn(double x)//判断浮点数符号
{
if (fabs(x) < eps)
return 0;
if (x < 0)
return -1;
return 1;
}
struct point
{
double x, y;
point() {}
point(double x, double y) : x(x), y(y) {}
point operator+(const point &p) const
{
point new_p(x + p.x, y + p.y);
return new_p;
}
point operator-(const point &p) const
{
point new_p(x - p.x, y - p.y);
return new_p;
}
point operator|(const double &p) const //数乘
{
point new_p(x * p, y * p);
return new_p;
}
point operator/(const double &p) const //同数乘
{
point new_p(x / p, y / p);
return new_p;
}
bool operator==(const point &p) const
{
return sgn(x - p.x) == 0 && sgn(y - p.y) == 0;
}
double operator*(const point &p) const //点乘
{
return x * p.x + y * p.y;
}
double operator^(const point &p) const //叉乘
{
return x * p.y - y * p.x;
}
double len() //原点距离
{
return sqrt(x * x + y * y);
}
void print()
{
cout << x << ' ' << y << '\n';
}
};
double dist(const point &a, const point &b) //两点距离
{
return sqrt((a - b) * (a - b));
}
//typedef point vec; 向量也可以这样表示
用 printf
输出双精度浮点型数一定要用 %f
,用 %lf
在一些情况下只会输出 -0.000000
!
凸包
Graham算法
找出最左下角的点,排序,然后一个一个连过去,如果新边的产生是逆时针的就入栈,如果是顺时针的就不断弹出(用叉积判断)。最后栈中的点就是凸包边界的点。
Andrew算法
按x,y轴坐标排序,依次向后连边,如果下一个点在上一条边的左边就入栈,在右边就不断出栈,可以得到半个凸壳。在另外半边也这样操作即可。
例题:P2742 [USACO5.1]圈奶牛Fencing the Cows /【模板】二维凸包
模板题,测试一下代码正确性
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
const int maxn = 1e5 + 10;
int sgn(double x)
{
if (fabs(x) < eps)
return 0;
if (x < 0)
return -1;
return 1;
}
struct point
{
double x, y;
point() {}
point(double x, double y) : x(x), y(y) {}
point operator+(const point &p) const
{
point new_p(x + p.x, y + p.y);
return new_p;
}
point operator-(const point &p) const
{
point new_p(x - p.x, y - p.y);
return new_p;
}
point operator|(const double &p) const //scalar multiplication
{
point new_p(x * p, y * p);
return new_p;
}
point operator/(const double &p) const
{
point new_p(x / p, y / p);
return new_p;
}
bool operator==(const point &p) const
{
return sgn(x - p.x) == 0 && sgn(y - p.y) == 0;
}
double operator*(const point &p) const //dot product
{
return x * p.x + y * p.y;
}
double operator^(const point &p) const //cross product
{
return x * p.y - y * p.x;
}
double len()
{
return sqrt(x * x + y * y);
}
void print()
{
cout << x << ' ' << y << '\n';
}
};
double dist(const point &a, const point &b)
{
return sqrt((a - b) * (a - b));
}
//typedef point vec;
point pt[maxn];
int Stack[maxn], top;
//相对于list[0]的极角排序
bool _cmp(point p1, point p2)
{
double tmp = (p1 - pt[0]) ^ (p2 - pt[0]);
if (sgn(tmp) > 0)
return true;
else if (sgn(tmp) == 0 && sgn(dist(p1, pt[0]) - dist(p2, pt[0])) <= 0)
return true;
else
return false;
}
void Graham(int n)
{
point p0;
int k = 0;
p0 = pt[0];
//找最下边的一个点
for (int i = 1; i < n; i++)
{
if ((p0.y > pt[i].y) || (p0.y == pt[i].y && p0.x > pt[i].x))
{
p0 = pt[i];
k = i;
}
}
swap(pt[k], pt[0]);
sort(pt + 1, pt + 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((pt[Stack[top - 1]] - pt[Stack[top - 2]]) ^ (pt[i] - pt[Stack[top - 2]])) <= 0)
top--;
Stack[top++] = i;
}
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i)
scanf("%lf%lf", &pt[i].x, &pt[i].y);
Graham(n);
double ans = 0.0;
Stack[top] = Stack[0];
for (int i = 0; i < top; ++i)
ans += dist(pt[Stack[i]], pt[Stack[i + 1]]);
printf("%.2lf\n", ans);
return 0;
}