凸包
凸包是什么?
想象在平面上有若干个钉子,将无限大的橡皮筋突然松开,勒紧后形成的图形称为凸包。
模板问题:平面上给定一堆点,求凸包
有两种算法可以求凸包,Graham算法和Andraw算法,这里说的是Andraw算法
第一步:将点排序,横坐标为第一关键字,纵坐标为第二关键字
第二步:从左到右维护上半部分,从右到左维护下半部分
第三步:再次维护起点
double area(pdd a,pdd b,pdd c)//面积
{
return cross(b-a,c-a);//叉积
}
double andraw()
{
sort(q,q+n);
int top=0;//栈顶元素
for(int i=0;i<n;i++)//从左到右枚举所有点
{
while(top>=2&&area(q[stk[top-1]],q[stk[top]],q[i])>=0)//左侧删除,通过area函数面积的正负判断在哪侧
{
// 凸包边界上的点即使被从栈中删掉,也不能删掉used上的标记
if(area(q[stk[top-1]],q[stk[top]],q[i])>0)
used[stk[top--]]=false;
else top--;
}
stk[++top]=i;
used[i]=true;
}
used[0]=false;//第一个点再更新一遍
for(int i=n-1;i>=0;i--)
{
if(used[i])
continue;
while(top>=2&&area(q[stk[top-1]],q[stk[top]],q[i])>=0)
top--;
stk[++top]=i;
}
double res=0;
for(int i=2;i<=top;i++)//起点两次
{
res+=get_dist(q[stk[i-1]],q[stk[i]]);
}
return res;
}
半平面交
半平面交解决这样一类问题:
给定很多有向直线,只保留直线左侧部分,求保留的交集
第一步:将所有向量按角度排序
求角度用actan2函数,此函数 y 在前,x 在后
第二步: 按顺序扫描所有向量,用双端队列维护轮廓
(直线用点向式存储)
double half_plane_intersection()
{
sort(line,line+cnt,cmp);//按照角度排序
int hh=0,tt=-1;
for(int i=0;i<cnt;i++)
{
//去掉重复角度
if(i&&!dcmp(get_angle(line[i]),get_angle(line[i-1]))) continue;
while(hh+1<=tt&&on_right(line[i],line[q[tt-1]],line[q[tt]]))//类似于求凸包
tt--;
while(hh+1<=tt&&on_right(line[i],line[q[hh]],line[q[hh+1]]))
hh++;
q[++tt]=i;
}
//队头更新队尾,队尾更新队头
while(hh+1<=tt&&on_right(line[q[hh]],line[q[tt-1]],line[q[tt]]))
tt--;
while(hh+1<=tt&&on_right(line[q[tt]],line[q[hh]],line[q[hh+1]]))
hh++;
q[++tt]=q[hh];
int k=0;
for(int i=hh;i<tt;i++)
ans[k++]=get_line_intersection(line[q[i]],line[q[i+1]]);//求两个函数交点的函数
double res=0;
for(int i=1;i+1<k;i++)
res+=area(ans[0],ans[i],ans[i+1]);//面积函数
return res/2;
}