这里的代码参照《算法导论》第三版第 33 章第 3 节的理论,以洛谷P2742圈奶牛为背景实现。
// lg-p2742
#include <algorithm>
#include <climits>
#include <cmath>
#include <cstdio>
#include <vector>
template<typename T>
T square(const T x) {
return x * x;
}
struct Point {
double x, y;
int quadrant() const {
// 此处可以直接与零比较是否相等,因为 0 是可以准确表达的。
if (x == 0.0 && y == 0.0) return 0; // 原点
else if (x > 0.0 && y == 0.0) return 1; // x正
else if (x > 0.0 && y > 0.0) return 2; // 第一象限
else if (x == 0.0 && y > 0.0) return 3; // y正
else if (x < 0.0 && y > 0.0) return 4; // 第二象限
else if (x < 0.0 && y == 0.0) return 5; // x负
else if (x < 0.0 && y < 0.0) return 6; // 第三象限
else if (x == 0.0 && y < 0.0) return 7; // y负
else return 8; // 第四象限
}
double crossProduct(const Point &other) const {
return x * other.y - y * other.x;
}
double distance(const Point &other) const {
return sqrt(square(x-other.x) + square(y-other.y));
}
Point operator-(const Point &other) const {
return Point { .x = x-other.x, .y = y-other.y };
}
bool operator==(const Point &other) {
return x == other.x && y == other.y;
}
};
using Vector2d = Point;
void sortPoints(std::vector<Point> &points) {
const int n = points.size();
Point bottomMost {.x = INT_MAX, .y = INT_MAX};
int indBottomMost;
for (int i = 0; i < n; ++i) {
// 找到 y 坐标最小的点
if (points[i].y < bottomMost.y) {
bottomMost = points[i];
indBottomMost = i;
}
}
points[indBottomMost] = points[0];
points[0] = bottomMost;
std::sort(points.begin()+1, points.end(), [&](const Point &a, const Point &b) -> bool {
// 以 points[0] 为极点,做极角排序。
const Vector2d oa = a - bottomMost;
const Vector2d ob = b - bottomMost;
if (oa.quadrant() != ob.quadrant()) return oa.quadrant() < ob.quadrant();
return oa.crossProduct(ob) > 0.0f;
});
}
bool sameAngle(const Vector2d &a, const Vector2d &b) {
if (a.quadrant() != b.quadrant()) return false;
else if (a.x == 0 || b.x == 0 || a.y == 0 || b.y == 0) return true;
return a.y / a.x == b.y / b.x;
}
// @return new size of <points>
int removeSameAngle(std::vector<Point> &points) {
const Point origin = points.front();
const int n = points.size();
int tail = 0;
for (int i = 1; i < n; ++i) {
if (sameAngle(points[i] - origin, points[tail] - origin)) {
if (origin.distance(points[i]) > origin.distance(points[tail])) {
points[tail] = points[i];
}
} else {
points[++tail] = points[i];
}
}
return tail+1;
}
bool leftTurn(const Point &o, const Point &p1, const Point &p2) {
const Vector2d v2 = p2 - o;
const Vector2d v1 = p1 - o;
return v2.crossProduct(v1) < 0.0;
}
// Graham Scan
double calcLengthOfFence(std::vector<Point> &points) {
int n = points.size();
if (n < 3) return 0;
sortPoints(points);
n = removeSameAngle(points);
std::vector<Point> convexHull; convexHull.reserve(3);
for (int i = 0; i < 3; ++i) {
convexHull.push_back(points[i]);
}
for (int i = 3; i < n; ++i) {
while (convexHull.size() > 2 && !leftTurn(convexHull[convexHull.size()-2], convexHull.back(), points[i])) {
convexHull.pop_back();
}
convexHull.push_back(points[i]);
}
double fenceLength = convexHull.front().distance(convexHull.back());
for (int i = convexHull.size()-1; i > 0; --i) {
fenceLength += convexHull[i].distance(convexHull[i-1]);
}
return fenceLength;
}
int main() {
int m;
scanf("%d", &m);
std::vector<Point> points(m);
for (int i = 0; i < m; ++i) {
scanf("%lf %lf", &points[i].x, &points[i].y);
}
printf("%.2lf\n", calcLengthOfFence(points));
return 0;
}