1 题意
在二维平面上给出一些城堡的坐标,要求用围墙把所有的城堡围起来,并且围墙与城堡的距离要不小于
r
r
r,求围墙至少多长。
链接:link。
2 思路
命题:当r=0时,围墙最小长度为凸包。当r>0时,围墙最小长度为凸包+2πr。
证明:由两点直线最短,反证即得。
2.1 Graham扫描法
下面介绍算法。
- 纵坐标最小的点 P 0 P_{0} P0一定是凸包上的点,将其加入栈中。
- 其余点以 P 0 P_{0} P0为基准进行极角排序。
- 对于每个点做如下处理。
- 如果栈只有一个元素或者从栈顶点到当前点所对应的向量在栈方向的左边则将当前点压入栈,否则出栈。
最终栈中的点组成凸包。
2.1.1 时间复杂度分析
极角排序所需要的时间为 O ( n l o g ( n ) ) \mathcal{O}(nlog(n)) O(nlog(n)),Graham扫描所需要的时间为 O ( n ) \mathcal{O}(n) O(n),所以时间复杂度为 O ( n l o g ( n ) ) \mathcal{O}(nlog(n)) O(nlog(n))。
2.1.2 实现
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1005;
const double PI=acos(-1.0);
struct Node{
int x,y;
}node[MAXN];
int stk[MAXN],top;
int cross(Node p0,Node p1,Node p2){
return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
double dis(Node p1,Node p2){
return sqrt((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
bool cmp(Node p1,Node p2){
int tmp=cross(node[0],p1,p2);
if(tmp>0) return true;
else return tmp==0&&dis(node[0],p1)<dis(node[0],p2);
}
void init(int n){
scanf("%d %d",&node[0].x,&node[0].y);
Node p0=node[0];
int ind=0;
for(int i=1;i<n;i++){
scanf("%d %d",&node[i].x,&node[i].y);
if((p0.y>node[i].y)||((p0.y==node[i].y)&&(p0.x>node[i].x))){
p0=node[i];
ind=i;
}
}
swap(node[0],node[ind]);
sort(node+1,node+n,cmp);
}
void graham(int n){
stk[top=0]=0;
for(int i=1;i<n;i++){
while(top&&cross(node[stk[top-1]],node[stk[top]],node[i])<=0) top--;
stk[++top]=i;
}
}
int main(){
int N,L;scanf("%d %d",&N,&L);
init(N);graham(N);
double res=0;
for(int i=0;i<top;i++) res+=dis(node[stk[i]],node[stk[i+1]]);
res+=dis(node[stk[0]],node[stk[top]]);
res+=2*PI*L;
printf("%d\n",(int)(res+0.5));
return 0;
}