凸包相关
凸包
二维凸包#
凸多边形是指所有内角大小都在
凸包就是指在平面内能包含所有给定点的最小凸多边形叫做凸包。
可以以下面的例子来形象理解一下。
下面是一堆木桩,农夫约翰想要围成一个围栏,需要保证所有的木桩都在围栏内,但是约翰想尽量用少的花费。
显然我们围着最外面的木桩围起来是最优的
所以我们可以这么理解凸包:平面凸包是指覆盖平面上
那我们怎么让程序围起最外面的木桩呢?
Graham 算法#
本质:
Graham 扫描算法维护一个凸壳,通过不断在凸壳中加入新的点和去除影响凸性的点,最后形成凸包。
凸壳就是凸包的一部分。
排序#
我们的 Graham 算法的第一步就是对点集进行排序,这样能保证其有序性,从而在后续的处理中更高效。
我们首先先择一个
剩下点集中按照极角的大小逆时针排序,然后编号为
我们按照排序结束时的顺序枚举每一个点,依次连线,这里可以使用一个栈来存储,每次入栈,如果即将入栈的元素与栈顶两个元素所构成了一个类似于凹壳的东西,那么显然处于顶点的那个点一定不在这个点集的凸包上,而他正好在栈顶,所以把它弹出栈,新点入栈。
但是,新来的点有可能既踢走了栈顶,再连接新的栈顶元素后却发现仍然可以踢出,此时就不能忘记判断。
总的时间复杂度为
Andrew 算法#
-
对所有点进行排序,以
为第一关键字,以 为第二关键字。第 两个点一定在凸包上。 -
先顺序枚举所有点,求下凸包。用栈来维护当前在凸包上的点;新点入栈前,总要判断该弹出哪些旧点。只要新点处在由栈顶两点构成的有向直线的右侧或共线,就弹出旧点。不能再弹出的时候,新点入栈。
-
逆序枚举所有点,求上凸包,用栈维护,和上面一样。
总时间复杂度为
其实共线的点是不用删的,但是后面计算距离的时候枚举会变多,虽然差不了多少
code:
#include<bits/stdc++.h>
#define int long long
#define DB double
#define N 1000100
using namespace std;
int n,top;
struct sb{DB x,y;}e[N],stk[N];
inline DB cross(sb a,sb b,sb c){return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);}
inline DB js(sb a,sb b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
inline int cmp(sb a,sb b){if(a.x==b.x)return a.y<b.y;return a.x<b.x;}
inline DB Andrew()
{
sort(e+1,e+n+1,cmp);
for(int i=1;i<=n;i++)
{
while(top>1&&cross(stk[top-1],stk[top],e[i])<=0)top--;
stk[++top]=e[i];
}
int t=top;
for(int i=n-1;i>=1;i--)
{
while(top>t&&cross(stk[top-1],stk[top],e[i])<=0)top--;
stk[++top]=e[i];
}
DB res=0;
for(int i=1;i<top;i++)res+=js(stk[i],stk[i+1]);
return res;
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>e[i].x>>e[i].y;
DB ans=Andrew();
printf("%.2lf\n",ans);
return 0;
}
这道题目也是一道模板,我们看到里面给了你哪些点是凸包的点,其实可以不用给,因为我们后面能求出来。
我们在算法上选择 Andrew 算法,因为最后要求点从
这里就体现了手写栈的好处。
#include<bits/stdc++.h>
#define int long long
#define DB double
#define N 1000100
using namespace std;
int t,n,top;char c;
struct sb{DB x,y;}e[N],stk[N];
inline DB cross(sb a,sb b,sb c){return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);}
inline DB js(sb a,sb b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
inline int cmp(sb a,sb b){if(a.x==b.x)return a.y<b.y;return a.x<b.x;}
inline void Andrew()
{
sort(e+1,e+n+1,cmp);
for(int i=1;i<=n;i++)
{
while(top>1&&cross(stk[top-1],stk[top],e[i])<0)top--;
stk[++top]=e[i];
}
int t=top;
for(int i=n-1;i>=1;i--)
{
while(top>t&&cross(stk[top-1],stk[top],e[i])<0)top--;
stk[++top]=e[i];
}
}
signed main()
{
cin>>t;
while(t--)
{
top=0;
cin>>n;
for(int i=1;i<=n;i++)cin>>e[i].x>>e[i].y>>c;
Andrew();
cout<<top-1<<endl;
for(int i=1;i<top;i++)cout<<(int)stk[i].x<<" "<<(int)stk[i].y<<endl;
}
return 0;
}
作者: 北烛青澜
出处:https://www.cnblogs.com/Multitree/p/17436350.html
本站使用「CC BY 4.0」创作共享协议,转载请在文章明显位置注明作者及出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!