这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战.
计算几何模板之一: 凸包
大致意思就是先极角排序,再用单调栈维护凸包
有参考计算几何之凸包模板
实现中,令s[cnt+1]=s[1],方便循环。
struct Pt{
double x,y;
}p[M], s[M];
//调用graham(n),将p[1..n]的点求凸包放在s[1..cnt]中,返回cnt
namespace Convex //求凸包
{
double cross(Pt a1, Pt a2, Pt b1, Pt b2)
{
return (a2.x-a1.x)*(b2.y-b1.y)-(b2.x-b1.x)*(a2.y-a1.y);
}
double dis(const Pt &p1, const Pt &p2)
{
return sqrt((p2.y-p1.y)*(p2.y-p1.y) + (p2.x-p1.x)*(p2.x-p1.x));
}
bool cmp(const Pt &p1, const Pt &p2)
{
double cj = cross(p[1], p1, p[1], p2);
if(cj>0) return 1;
if(cj==0 && dis(p[1], p1)<dis(p[1], p2)) return 1;
return 0;
}
int graham(int n)
{
int k = 1;
for(int i=1; i<=n; ++i)
if(p[i].y<p[k].y || (p[i].y==p[k].y && p[i].x<p[k].x))
k = i;
swap(p[1], p[k]);
sort(p+2, p+n+1, cmp);
int cnt = 0;
s[++cnt] = p[1];
for(int i=2; i<=n; ++i)
{
while(cnt>1 && cross(s[cnt-1],s[cnt],s[cnt],p[i])<=0)
--cnt;
s[++cnt] = p[i];
}
s[cnt+1] = p[1];
return cnt;
}
}
习题
- HDU-1392 Surround the Trees,求凸包周长,注意只有两个点时输出线段长即可。
int main(void)
{
int n;
while(cin >> n && n)
{
for(int i=1; i<=n; ++i)
p[i].x = read(), p[i].y = read();
int cnt = Convex::graham(n);
double ans = 0;
if(n==2)
ans = Convex::dis(s[1],s[2]);
else for(int i=1; i<=cnt; ++i)
ans += Convex::dis(s[i], s[i+1]);
printf("%.2f\n",ans );
}
return 0;
}
- POJ-3348 Cows 计算凸包面积,n条线段的端点都和原点连起来,求叉积之和,再除以2就是面积。
int main(void)
{
int n;
while(cin >> n && n)
{
for(int i=1; i<=n; ++i)
p[i].x = read(), p[i].y = read();
int cnt = Convex::graham(n);
double ans = 0;
for(int i=1; i<=cnt; ++i)
ans += Convex::cross(s[0], s[i], s[0], s[i+1]);
printf("%d\n",(int)ans/100 );
}
return 0;
}
Prim 算法
Prim算法的流程
- 将节点分为在生成树中的点集合与不在生成树中的点集合,初始任选一个节点加入中。
- 遍历所有与直接相连的中的点,从这些点中选择距离最小的一个,将其加入,这条边加入到最小生成树中。
- 重复操作2,直到所有节点都在最小生成树中。
Prim算法与Dijkstra算法 运行结束后会得到一棵单源最短路树,将节点加入到最短路树的过程与Prim算法的流程极为相似。
唯一的区别只在于,dijkstra选择节点的标准是距离起点最近,而prim选择节点的标准是距离树中任意一个节点最近。
时间复杂度 优先队列实现: 数组实现:
本文也发表于我的 CSDN 博客中。