凸包

1. Luogu P2742 [USACO5.1]圈奶牛Fencing the Cows /【模板】二维凸包

上面是板子题

Andrew 算法

  1. 对所有点按坐标 x 为第一关键字、 y 为第二关键字排序。第1、第n两个点一定在凸包上。
  2. 顺序枚举所有点,求下凸包。用维护当前在凸包上的点:新点入栈前,总要判断该弹出哪些旧点。只要新点处在由栈顶两点构成的有向直线的右侧或共线,就弹出旧点。不能弹出时,新点入栈。
  3. 逆序枚举所有点,求上凸包。用维护同上。

注意:每个点入栈两次,出栈不超过两次,所以总次数不超过 。

注意:最后封口时,栈底和栈顶存的都是第一个点。

时间:O(nlogn)

复制代码
 1 #include <iostream>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <cmath>
 5 using namespace std;
 6 
 7 const int N=100010;
 8 struct Point{double x,y;} p[N],s[N];
 9 int n,top;
10 
11 double cross(Point a,Point b,Point c){ //叉积
12   return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
13 }
14 double dis(Point a,Point b){ //距离
15   return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
16 }
17 bool cmp(Point a, Point b){ //比较
18   return a.x!=b.x ? a.x<b.x : a.y<b.y;
19 }
20 double Andrew(){
21   sort(p+1,p+n+1,cmp); //排序
22   for(int i=1; i<=n; i++){ //下凸包
23     while(top>1&&cross(s[top-1],s[top],p[i])<=0)top--;
24     s[++top]=p[i];
25   }
26   int t=top;
27   for(int i=n-1; i>=1; i--){ //上凸包
28     while(top>t&&cross(s[top-1],s[top],p[i])<=0)top--;
29     s[++top]=p[i];
30   }
31   double res=0; //周长
32   for(int i=1; i<top; i++) res+=dis(s[i],s[i+1]);
33   return res;
34 }
35 int main(){
36   scanf("%d",&n);
37   for(int i=1;i<=n;i++)scanf("%lf%lf",&p[i].x,&p[i].y);
38   printf("%.2lf\n", Andrew());
39   return 0;
40 }
复制代码

 

2. Luogu P3829 [SHOI2012]信用卡凸包

思路:圆弧之和=圆的周长

   直边之和=所有圆心构成的凸包的周长

时间:O(nlogn)

复制代码
 1 #include <bits/stdc++.h>  //
 2 using namespace std;
 3 #define endl '\n'
 4 int _,k, m;
 5 
 6 const double pi = acos(-1);
 7 
 8 
 9 typedef pair<double, double> point;
10 point p[N], s[N];
11 int n, top, cnt;
12 
13 point operator-(point a,point b){ //向量-
14   return point(a.x-b.x,a.y-b.y);
15 }
16 
17 double dis(point a, point b) {  //距离
18   return sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y));
19 }
20 
21 double cross(point a, point b, point c) {  //叉积
22   return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
23 }
24 
25 //逆转角
26 point rotate(point a, double b) {  //将某个向量逆时针旋转b°
27   return point(a.x * cos(b) - a.y * sin(b), a.x * sin(b) + a.y * cos(b));
28 }
29 
30 bool cmp(point a, point b) { return a.x != b.x ? a.x < b.x : a.y < b.y; }
31 
32 double Andrew() {
33   sort(p + 1, p + 1 + n);
34   for (int i = 1; i <= n; i++) {  //下凸包
35     //在栈顶和顶下一个元素构成的有向线段的右侧则入栈,左侧弹出上一个入栈的点
36     while (top > 1 && cross(s[top - 1], s[top], p[i]) <= 0) top--;
37     s[++top] = p[i];
38   }
39   int t = top;                        //中间变量存top
40   for (int i = n - 1; i >= 1; i--) {  //上凸包
41     while (top > t && cross(s[top - 1], s[top], p[i]) <= 0) top--;
42     s[++top] = p[i];
43   }
44   double ans = 0;
45   for (int i = 1; i < top; i++) ans += dis(s[i], s[i + 1]);
46   return ans;
47 }
48 
49 void solve() {
50   cin >> n;
51   double a, b, r;
52   cin >> a >> b >> r;
53   a = a / 2 - r, b = b / 2 - r;  //圆心到中心的边距
54   int dx[] = {1, 1, -1, -1}, dy[] = {1, -1, -1, 1};
55   while (n--) {
56     double x, y, z;
57     cin >> x >> y >> z;
58     for (int i = 0; i < 4; i++) {
59       point t = rotate({dx[i] * b, dy[i] * a}, z);  //四个圆心旋转
60       p[++cnt] = {x + t.x, y + t.y};                //中间圆心平移
61     }
62   }
63   n = cnt;  //圆心个数
64   printf("%.2lf\n", Andrew() + 2 * pi * r);
65 }
66 
67 int main() {
68   // cin >> _;
69   _ = 1;
70   while (_--) {
71     solve();
72   }
73   return 0;
74 }
复制代码

练习:

POJ1113 Wall

思路:答案就是凸包周长 + 半径为 L 的圆的周长

Luogu P4724 【模板】三维凸包

Luogu P2287 [HNOI2004]最佳包裹

posted @   rw156  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示