前言:本菜鸡的第一篇算几,记录一下我有多菜
Graham算法
一种凸包算法,扫描部分的时间复杂度为,总的时间复杂度
原理:从点集中先找出一个最左下方的点,易证这个点肯定在凸包上,然后以这点为极点,将所有点根据与这点的极角进行排序,并且同时使用一个栈结构维护凸包上的点。按照极角序依次将当前点与栈顶的两个点作拐向判断:若右拐,则将当前点加入栈中,否则将栈顶点弹出。当点集遍历完之后,还在栈中的点就是凸包上的点。
算法步骤
- 对所有点进行排序,选择坐标最小的点作为极点,即找到第一个一定在凸包上的点
bool cmp1(const Pnt &a,const Pnt &b){
return a.x!=b.x?a.x<b.x:a.y<b.y;
}
- 将其余所有点按照极角排序,在极角相同的情况下比较与极点的距离,按极角序依次处理每一个点
inline bool cmp2(const Pnt &st,const Pnt &ed){//按极角排序
int del=p[st]*p[ed];
if(del!=0)return del>0;
return (getdis(p[st]-p[1])-getdis(p[ed]-p[1]))<eps;
}
- 用一个栈储存凸包上的点,先将按极角和极点排序最小的两个点入栈(其实这个地方可以用重载运算符来写,但是不知道为什么我语法一直报错来着qwq
一定是我太菜辽)
db getdis(const Pnt &a,const Pnt &b){return (db)sqrt((db)(a.x-b.x)*(a.x-b.x)*1.0+(db)1.0*(a.y-b.y)*(a.y-b.y));}
db cj(Pnt a1,Pnt a2,Pnt b1,Pnt b2){return (db)(a2.x-a1.x)*(b2.y-b1.y)-(b2.x-b1.x)*(a2.y-a1.y);}
inline bool cmp2(Pnt st,Pnt ed){
db tmp=cj(p[1],st,p[1],ed);
if(tmp>0)return 1;
if(tmp==0&&getdis(p[0],st)<(getdis(p[0],ed)))return 1;
return 0;
}
- 按序扫描每个点,检查栈顶的前两个元素与这个点构成的折线段是否向左偏(叉积零)(即构成的有向面积为负或共线,共线可以根据和极点距离来算,不过前面已经做过处理,这里就不用了)
- 如果满足,则弹出栈顶元素,并返回再次检查,直到不满足,将该点入栈,并对其余点不断执行此操作
for(int i=2;i<=n;i++){
while(tot>1&&cj(sta[tot-1],sta[tot],sta[tot],p[i])<=0)tot--;
tot++;
sta[tot]=p[i];
}
- 最终栈中元素为凸包的顶点序列
一道板子题
P2742 【模板】二维凸包 / [USACO5.1]圈奶牛Fencing the Cows
#include <bits/stdc++.h>
#define db double
#define eps 1e-8
using namespace std;
const int N=(1e5)+50;
int n;
double ans,mid;
inline int read(){
int cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<1)+(cnt<<3)+(c&15);c=getchar();}
return cnt*f;
}
struct Pnt{double x,y;}p[N],sta[N];
db getdis(const Pnt &a,const Pnt &b){return (db)sqrt((db)(a.x-b.x)*(a.x-b.x)*1.0+(db)1.0*(a.y-b.y)*(a.y-b.y));}
db cj(Pnt a1,Pnt a2,Pnt b1,Pnt b2){return (db)(a2.x-a1.x)*(b2.y-b1.y)-(b2.x-b1.x)*(a2.y-a1.y);}
inline bool cmp2(Pnt st,Pnt ed){
db tmp=cj(p[1],st,p[1],ed);
if(tmp>0)return 1;
if(tmp==0&&getdis(p[0],st)<(getdis(p[0],ed)))return 1;
return 0;
}
int main(){
n=read();
for(int i=1;i<=n;i++){
scanf("%lf%lf",&p[i].x,&p[i].y);
if(i!=1&&p[1].y>p[i].y){
swap(p[i],p[1]);
}
}
sort(p+2,p+1+n,cmp2);
sta[1]=p[1];
int tot=1;
for(int i=2;i<=n;i++){
while(tot>1&&cj(sta[tot-1],sta[tot],sta[tot],p[i])<=0)tot--;
tot++;
sta[tot]=p[i];
}
sta[tot+1]=p[1];
//因为这里是要构成一个圈,所以最后一个点一定要加入栈!!!
for(int i=1;i<=tot;i++) ans+=getdis(sta[i],sta[i+1]);
printf("%.2lf",ans);
return 0;
}
来源:CSDN
作者:Young_Zn_Cu
链接:https://blog.csdn.net/weixin_42750325/article/details/103737247