POJ 1113 Wall

最基础的凸包题,可以用来测模板。

凸包就是给定一些点,求包括这些点的边长最小的多边形,常用的有graham算法和melkman算法。

graham算法是先按y坐标再按x坐标排序,由凸包的性质可以发现最下面最左边的点一定是凸包上的点,然后按顺序扫

直到有一个点不满足与当前栈顶两点的左旋性质,然后退栈,直到他满足左旋或者堆栈里面还有一个元素,将它加入堆

栈,先从前往后遍历,再从后往前遍历,这样堆栈中所保存的就是最终的凸包上的点。

本题只要把凸包构造出来然后,依次加入凸包上相邻两点的距离,再加上圆的周长即可。

#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
double pi=acos(-1.0);
struct Point{
	int x,y;
}p[1005];
int n,top;
Point stack[1005];
double dist(Point p1,Point p2)				//点距
{
	return sqrt((double)(p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
bool oneLine(Point p1,Point p2,Point p3)			//判断三点是否共线
{
	return !((p2.y-p1.y)*(p3.x-p2.x)-(p3.y-p2.y)*(p2.x-p1.x));
}
int cmp(Point p1,Point p2)
{
	return (p1.y<p2.y)||(p1.y==p2.y && p1.x<p2.x);
}
bool mul(Point p1,Point p2,Point p3)			//p1p2和p1p3的叉积
{
	return (p2.x-p1.x)*(p3.y-p1.y)>(p2.y-p1.y)*(p3.x-p1.x);
}
void graham()						//算法本体
{
	int i;
	stack[0]=p[0];
	stack[1]=p[1];
	top=1;
	for(i=2;i<n;i++)
	{
		while(top && (!mul(stack[top],stack[top-1],p[i])))
			top--;
		stack[++top]=p[i];
	}			//直到加入的点满足左旋
	int len = top;
	stack[++top] = p[n - 2];
	for(i = n - 3; i >= 0; i--)
	{
		while(top != len && (!mul(stack[top], stack[top-1], p[i])))
			top--;
		stack[++top] = p[i];
	}			//反向遍历
}
int main()
{
	int i,r;
	while(scanf("%d %d",&n,&r)!=EOF)
	{
		double d=0;
		for(i=0;i<n;i++)
			scanf("%d %d",&p[i].x,&p[i].y);
		sort(p,p+n,cmp);
		graham();
		for(i=0;i<top;i++)
			d+=dist(stack[i],stack[i+1]);
		d+=2*pi*r;
		printf("%.0f\n",d);
	}
	return 0;
}

melkman算法:适用于已经构成简单多边形的点的组合,将第一个点加入堆栈,先找到连续的两个点使得他们和之前的点不共线,将前一个点加入堆栈尾,将后一个点分别加入堆栈首和尾,如果三个点的叉积小于零就把第一个点和第二个点个点互换,每引入一个新的点,判断它和栈首和栈尾是否在凸包内,如果不在则将栈首和栈尾的元素分别递归退栈,直到栈首和栈尾和他都构成一个左旋和右旋,这就相当于栈尾,栈首,栈尾构成一个循环的凸包。
#include<stdio.h>
#include<math.h>
double pi=acos(-1.0);
struct Point{
	int x,y;
}p[1005];
int n,front,rear;
Point stack[2010];
double dist(Point p1,Point p2)				//点距
{
	return sqrt((double)(p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
int mul(Point p1,Point p2,Point p3)			//叉积
{
	return (p2.x-p1.x)*(p3.y-p1.y)-(p2.y-p1.y)*(p3.x-p1.x);
}
void melkman()
{
	int i;
	stack[n]=p[0];
	for(i=1;i<n;i++)				//加入不共线的三点
	{
		stack[n+1]=p[i];
		if(mul(stack[n],stack[n+1],p[i+1]))
			break;
	}
	front=n,rear=n+1;
	stack[--front]=stack[++rear]=p[i+1];
	/*printf("%d %d %d %d\n",stack[front],stack[rear]);*/
	if(mul(stack[n],stack[n+1],stack[n+2])<0)		//若右旋,则将stack[n]和stack[n+1]交换
	{
		Point tmp=stack[n];
		stack[n]=stack[n+1];
		stack[n+1]=tmp;
	}
	for(i=i+2;i<n;i++)
     {
         if(mul(stack[rear-1],stack[rear],p[i])>0 && mul(stack[front],stack[front+1],p[i])>0)		//在凸包内则不加入,否则加入堆栈使得堆栈中依旧满足凸包性质
             continue;
         while(mul(stack[rear-1],stack[rear],p[i])<=0)
             rear--;
         stack[++rear]=p[i];
         while(mul(stack[front],stack[front+1],p[i])<=0)
             front++;
         stack[--front]=p[i];
     }
	
}
int main()
{
	int i,r;
	while(scanf("%d %d",&n,&r)!=EOF)
	{
		double d=0;
		for(i=0;i<n;i++)
			scanf("%d %d",&p[i].x,&p[i].y);
		melkman();
		for(i=front;i<rear;i++)
			d+=dist(stack[i],stack[i+1]);
		d+=2*pi*r;
		printf("%.0f\n",d);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值