G52 凸包 Andrew算法

视频链接:https://www.bilibili.com/video/BV1XL411C7Pf/

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

Andrew 算法

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

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

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

时间:O(nlogn)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

const int N=100010;
struct Point{double x,y;} p[N],s[N];
int n,top;

double cross(Point a,Point b,Point c){ //叉积
  return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
double dis(Point a,Point b){ //距离
  return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool cmp(Point a, Point b){ //比较
  return a.x!=b.x ? a.x<b.x : a.y<b.y;
}
double Andrew(){
  sort(p+1,p+n+1,cmp); //排序
  for(int i=1; i<=n; i++){ //下凸包
    while(top>1&&cross(s[top-1],s[top],p[i])<=0)top--;
    s[++top]=p[i];
  }
  int t=top;
  for(int i=n-1; i>=1; i--){ //上凸包
    while(top>t&&cross(s[top-1],s[top],p[i])<=0)top--;
    s[++top]=p[i];
  }
  double res=0; //周长
  for(int i=1; i<top; i++) res+=dis(s[i],s[i+1]);
  return res;
}
int main(){
  scanf("%d",&n);
  for(int i=1;i<=n;i++)scanf("%lf%lf",&p[i].x,&p[i].y);
  printf("%.2lf\n", Andrew());
  return 0;
}

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

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

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

时间:O(nlogn)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define x first
#define y second
using namespace std;

const int N = 40010;
const double pi=acos(-1.0);
typedef pair<double,double> Point;
Point p[N],s[N];
int n,top,cnt;

double cross(Point a,Point b,Point c){ //叉积
  return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
double dis(Point a,Point b){ //距离
  return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
Point rotate(Point a, double b){ //逆转角
  return {a.x*cos(b)-a.y*sin(b), a.x*sin(b)+a.y*cos(b)};
}
double Andrew(){
  sort(p+1,p+n+1);
  for(int i=1; i<=n; i++){ //下凸包
    while(top>1&&cross(s[top-1],s[top],p[i])<=0)top--;
    s[++top]=p[i];
  }
  int t=top;
  for(int i=n-1; i>=1; i--){ //上凸包
    while(top>t&&cross(s[top-1],s[top],p[i])<=0)top--;
    s[++top]=p[i];
  }
  double res=0; //周长
  for(int i=1; i<top; i++) res+=dis(s[i],s[i+1]);
  return res;
}
int main(){
  scanf("%d",&n);
  double a,b,r;
  scanf("%lf%lf%lf",&a,&b,&r);
  a=a/2-r, b=b/2-r; //圆心到中心的边距
  int dx[]={1,1,-1,-1},dy[]={1,-1,-1,1}; //四角偏移量
  while(n--){
    double x,y,z;
    scanf("%lf%lf%lf",&x,&y,&z);
    for(int i=0; i<4; i++){
      Point t=rotate({dx[i]*b,dy[i]*a},z); //圆心旋转
      p[++cnt]={x+t.x,y+t.y}; //圆心平移
    }
  }
  n=cnt; //圆心个数
  printf("%.2lf\n", Andrew()+2*pi*r);
  return 0;
}
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define x first
#define y second
using namespace std;

const int N = 40010;
const double pi=acos(-1.0);
typedef pair<double,double> Point;
Point p[N],s[N];
int n,top,cnt;

Point operator-(Point a,Point b){ //向量-
  return Point(a.x-b.x,a.y-b.y);
}
double operator*(Point a,Point b){ //叉积
  return a.x*b.y-a.y*b.x;
}
double dis(Point a,Point b){ //距离
  return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
Point rotate(Point a, double b){ //逆转角
  return {a.x*cos(b)-a.y*sin(b), a.x*sin(b)+a.y*cos(b)};
}
double Andrew(){
  sort(p+1,p+n+1);
  for(int i=1; i<=n; i++){ //下凸包
    while(top>1&&(s[top]-s[top-1])*(p[i]-s[top-1])<=0)top--;
    s[++top]=p[i];
  }
  int t=top;
  for(int i=n-1; i>=1; i--){ //上凸包
    while(top>t&&(s[top]-s[top-1])*(p[i]-s[top-1])<=0)top--;
    s[++top]=p[i];
  }
  double res=0; //周长
  for(int i=1; i<top; i++) res+=dis(s[i],s[i+1]);
  return res;
}
int main(){
  scanf("%d",&n);
  double a,b,r;
  scanf("%lf%lf%lf",&a,&b,&r);
  a=a/2-r, b=b/2-r; //圆心到中心的边距
  int dx[]={1,1,-1,-1},dy[]={1,-1,-1,1}; //四角偏移量
  while(n--){
    double x,y,z;
    scanf("%lf%lf%lf",&x,&y,&z);
    for(int i=0; i<4; i++){
      Point t=rotate({dx[i]*b,dy[i]*a},z); //圆心旋转
      p[++cnt]={x+t.x,y+t.y}; //圆心平移
    }
  }
  n=cnt; //圆心个数
  printf("%.2lf\n", Andrew()+2*pi*r);
  return 0;
}

 

练习:

POJ1113 Wall

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

Luogu P4724 【模板】三维凸包

Luogu P2287 [HNOI2004]最佳包裹

 

posted @ 2023-02-27 15:48  董晓  阅读(1045)  评论(0编辑  收藏  举报