题目大意:
给出平面上若干个点的坐标,你的任务是建一个环形围墙,把所有的点围在里面,且距所有点的距离不小于l。求围墙的最小长度。
思路:
很容易得出答案就是凸包周长+以l为半径的圆的周长。
这里讲一下Andrew算法。
Andrew是Graham算法的变种,而且Andrew更快,更稳定。
Andrew算法思想是先将n个点按照x坐标从小到大排序(x相同按照y从小到大),得到一个序列a1,a2,...an,将a1,a2放入ch数组,从a3开始,判断点是否在凸包当前前进方向的左边,如果是,就将点加入ch数组,否则就删除ch中的点直到在左边为止,重复上述操作。值得注意的是,Andrew需要做2次上述过程,第一次求出“下凸包”,第二次求出“上凸包”,合并起来就是完整的凸包。
具体看代码。
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; #define pi acos(-1.0) struct point{ int x,y; point(int x=0,int y=0):x(x),y(y){} }a[1001],ch[1001]; typedef point Vector; point operator - (point a,point b){ return point(a.x-b.x,a.y-b.y); } int cross(Vector a,Vector b){ return(a.x*b.y-a.y*b.x); } double length(Vector a){ return(sqrt((double)a.x*a.x+a.y*a.y)); } bool cmp(point a,point b){ return(a.x<b.x||(a.x==b.x&&a.y<b.y)); } int n,i,j,k,l,x,y; double sum=0; int main(){ scanf("%d%d",&n,&l); for(i=1;i<=n;++i)scanf("%d%d",&a[i].x,&a[i].y); sort(a+1,a+n+1,cmp); int m=0; for(i=1;i<=n;++i){ while(m>1&&cross(ch[m]-ch[m-1],a[i]-ch[m-1])<0)m--; ch[++m]=a[i]; } k=m; for(i=n-1;i>=1;--i){ while(m>k&&cross(ch[m]-ch[m-1],a[i]-ch[m-1])<=0)m--; ch[++m]=a[i]; } for(i=1;i<m;++i)sum+=length(ch[i+1]-ch[i]); printf("%.0f\n",sum+2*pi*l); return 0; }