最基础的凸包题,可以用来测模板。
凸包就是给定一些点,求包括这些点的边长最小的多边形,常用的有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;
}