学习记录
兴起捡起很久以前就研究过的凸包。
还是没有在学校共享里找到资料……
没有办法,只能又推荐dalao的博客了……
配合板题食用更佳!
P2742 【模板】二维凸包 / [USACO5.1]圈奶牛Fencing the Cows
进入正题
凸包可广泛运用于各种求最小点覆盖问题,方法千奇百怪,这里只列举三种。
涉及到向量的应用。
给出 n n 个点的坐标,求最短的能够围住这些点的多边形的周长。
引用一张经典的图:
法一:暴力枚举法
暴力出奇迹,水法真神奇
基本思想
枚举两点,判断其余点是否在这两点所在直线的一侧:
若是,则这两点为凸包上的点;
若不是,则继续枚举。
法二:Graham扫描法
基本思想
- 以坐标最小的点为原点,建系
- 将点按照极角大小排序
- 将当前点与栈顶边比较:若当前点在栈顶边的右侧,弹出栈顶点,重复当前步骤,否则进行下一步
- 若当前点在栈顶边的左侧,将当前点加入栈,更换当前点
时间复杂度: O(nlogn) O ( n log n )
法三:Melkman算法
Melkman算法是当前世界上公认的最优秀求凸包算法。
已知上述两种算法都是离线算法,而Melkman算法是在线算法。
时间复杂度: O(n) O ( n ) (显然优于目前其他任何算法)
经过反复斟酌,这位dalao的博客是比较详细且优秀的,这里不再赘述。
(你就是想掩饰你不会)具体用途与实现
P2742 【模板】二维凸包 / [USACO5.1]圈奶牛Fencing the Cows
Code
//不知道CSDN的Markdown是不是萎了,颜色渲染有点问题,凑合着看吧,也不知道什么时候修。 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; struct Dot { double x, y; int pos; Dot(double _x = 0, double _y = 0) { x = _x, y = _y; } }; struct Line { Dot p, v; Line() {} Line(Dot _p, Dot _v) { p = _p, v = _v; } }; Dot operator+(Dot a, Dot b) {return Dot(a.x + b.x, a.y + b.y);} Dot operator-(Dot a, Dot b) {return Dot(a.x - b.x, a.y - b.y);} Dot operator*(Dot a, double b) {return Dot(a.x * b, a.y * b);} double DotTimes(Dot a, Dot b) {return a.x * b.x + a.y * b.y;} double CrossTimes(Dot a, Dot b) {return a.x * b.y - a.y * b.x;} double Len(Dot a) {return sqrt(DotTimes(a, a));} double Dist(Dot a, Dot b) {return Len(a - b);} bool Direct(Dot a, Dot b) {return CrossTimes(a, b) < 0;} const int MAXN = 10000; const int Maxint = 2147483647; int n; Dot src = Dot(0, Maxint); Dot d[MAXN + 1]; int q[MAXN + 1]; double ans; bool cmp(Dot a, Dot b) { double tmp = CrossTimes(a, b); if (tmp == 0) return Len(a) < Len(b); else return tmp > 0; } int main(int argc, char const *argv[]) { //freopen("init.in", "r", stdin); scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%lf%lf", &d[i].x, &d[i].y); d[i].pos = i; if (d[i].y < src.y) src = d[i]; } for (int i = 1; i <= n; i++) { int tmp = d[i].pos; d[i] = d[i] - src; d[i].pos = tmp; } sort(d + 1, d + n + 1, cmp); for (int i = 1; i <= n; i++) { while ((Direct(d[q[q[0]]] - d[q[q[0] - 1]], d[i] - d[q[q[0]]])) && (q[0] > 2)) q[0]--; q[++q[0]] = i; } q[q[0] + 1] = q[1]; for (int i = 1; i <= q[0]; i++) ans += Len(d[q[i + 1]] - d[q[i]]); printf("%.2lf\n", ans); return 0; }