POJ1113 Wall 凸包(Graham扫描法)

Problem Address:http://poj.org/problem?id=1113


【前言】


我还是觉得这道题有点问题的。

正确答案是算出凸包再加上以L为半径的圆的周长。

但是,我觉得事实并非如此。

因为在有些凹进去的地方是需要把围墙造到里面去的,那种情况的围墙长度会更短。

不过既然答案是这样,那就只好这样做了。


【思路】


简单的凸包。

答案为凸包上点所围成的长度加上以L为半径的圆的周长。

因为需要围墙距离城堡L的距离,所以在拐角处需要画圆。

对于一个封闭的凸多边形,所有弧加起来就是一个圆。

画画就知道。

其实是刚好想学凸包,所以就找了道题来做。

具体的凸包看看算法导论,里面写的很清楚,实现也比较容易。

不要注意的是关于极角和左转的计算。

此外,输出时应该用printf("%.0lf\n", sum)。

一开始也成printf("%d\n", (int)sum),返回了WA。

后来找了一组1000个的数据,居然自己的程序跑不出来。

改了之后就AC了。

前者是四舍五入,后者是直接去尾。


【代码】


#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

const int maxn = 1000;
const double pi = 3.141592653;

struct point
{
	int x;
	int y;
}pt[maxn+5], q[maxn+5];

bool cmp(const point &a, const point &b)//比较极角
{
	int ax = a.x-pt[0].x;
	int ay = a.y-pt[0].y;
	int bx = b.x-pt[0].x;
	int by = b.y-pt[0].y;
	int t = ax*by - bx*ay;
	if (t<0) return 0;
	else if (t>0) return 1;
	else return (ax*ax+ay*ay)>(bx*by+by*by);
}

int main()
{
	int n, l;
	int i, k;
	point temp;
	scanf("%d %d", &n, &l);
	for (i=0; i<n; i++)
		scanf("%d %d", &pt[i].x, &pt[i].y);
	for (i=1, k=0; i<n; i++)//得到左下方坐标k
	{
		if (pt[i].y<pt[k].y)
		{
			k = i;
		}
		else if (pt[i].y==pt[k].y)
		{
			if (pt[i].x<pt[k].x)
			{
				k = i;
			}
		}
	}
	temp = pt[0];
	pt[0] = pt[k];
	pt[k] = temp;
	sort(pt+1, pt+n, cmp);//按极角排序
	q[0] = pt[0];//p0,p1,p2入栈
	q[1] = pt[1];
	q[2] = pt[2];
	int top = 3;
	pt[n] = pt[0];
	for (i=3; i<=n; i++)
	{
		while((pt[i].x-q[top-2].x)*(q[top-1].y-q[top-2].y)-(q[top-1].x-q[top-2].x)*(pt[i].y-q[top-2].y)>=0 && top>2)//判断左转,等于零去除共线
		{
			top--;
		}
		q[top] = pt[i];
		top++;
	}
	double sum = 2*pi*l;
	for (i=1; i<top; i++)
	{
		sum += sqrt((double)((q[i].x-q[i-1].x)*(q[i].x-q[i-1].x)+(q[i].y-q[i-1].y)*(q[i].y-q[i-1].y)));//计算距离
	}
	printf("%.0lf\n", sum);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值