【总结】Graham求凸包 及 习题

计算几何模板之一: 凸包

大致意思就是先极角排序,再用单调栈维护凸包

有参考计算几何之凸包模板

实现中,令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;
	}
}

习题

  1. 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;
}
  1. 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;
}

你可能感兴趣的:(题解)