题意
求把给出的点围起来一面墙且距离最小为L
分析
根据下图可以知道其实就是求凸包加上一个半径为L的圆周长
那么就是一个裸的凸包问题啦
一般来说求凸包有两种做法,卷包裹法和 Graham-Scan
看起来卷包裹更优秀呢(对于这题
卷包裹的原理比较简单:先找一个最边缘的点(一般是最左下角的),假设有一条绳子,以该点为端点向右边逆时针旋转直到碰到另一个点为止,此时找出凸包的一条边;然后再用新的点作为端点,继续旋转绳子,找到下一个端点;重复这一步骤直至围成一个凸多边形,即可得到这个点集的凸包。时间复杂度为O(n^2)
步骤:
i)选择点集左下角的点,该点为凸包的第一个点
ii)以水平向右的方向做射线,逆时针旋转,选择第一条在初始射线之上的射线作为当前射线,当前射线经过凸包的第二个点。
iii)以当前射线为基准,继续逆时针旋转至最靠近该射线的一条射线,找到凸包的下一个点。把这条射线作为基准重复上述操作,至到回到起始点。
通过当前基准射线寻找下一个射线有以下几种方式:
i)把每一条射线与其他n-2条线比较每步效率是o(n^2)
ii)通过计算各射线与当前基准射线的夹角的方式,效率是o(n)但是会存在浮点数误差。
ii)利用叉积运算求各射线斜率的相对关系,从而求得下一条射线,效率o(n)且不存在误差
代码:
#pragma warning(disable:4786)//使命名长度不受限制
#pragma comment(linker, "/STACK:102400000,102400000")//手工开栈
#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <stack>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d,&x,&y,&z)
#define rdl(x) scanf("%I64d,&x);
#define rds(x) scanf("%s",x)
#define rdc(x) scanf("%c",&x)
#define ll long long int
#define ull unsigned long long
#define maxn 1111
#define mod 1000000007
#define INF 0x3f3f3f3f //int 最大值
#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)
#define MT(x,i) memset(x,i,sizeof(x))
#define PI acos(-1.0)
#define E exp(1)
#define eps 1e-8
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll mul(ll a,ll b,ll p){ll sum=0;for(;b;a=(a+a)%p,b>>=1)if(b&1)sum=(sum+a)%p;return sum;}
inline void Scan(int &x) {
char c;while((c=getchar())<'0' || c>'9');x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
using namespace std;
int sgn(double x){
if(fabs(x)<eps)return 0;
if(x<0)return -1;
return 1;
}
struct Point{
double x,y;
Point(){}
Point(double _x,double _y){
x=_x;
y=_y;
}
bool operator <(const Point &b)const{
return sgn(x-b.x)==0?sgn(y-b.y):x<b.x;
}
double distance(const Point &p)const{
return hypot(x-p.x,y-p.y);
}
}my[maxn];
bool crossLeft(Point p1,Point p2,Point p3){
return ((p3.x-p1.x)*(p2.y-p1.y)-(p2.x-p1.x)*(p3.y-p1.y))<0;
}
int ans[maxn],sta[maxn],tail,cnt,n,l;
void jarvis(){
tail=cnt=0;sort(my,my+n);
sta[tail++]=0;sta[tail++]=1;
for(int i=2;i<n;++i){
while(tail>1&&!crossLeft(my[sta[tail-1]],my[sta[tail-2]],my[i]))tail--;
sta[tail++]=i;
}
for(int i=0;i<tail;++i)ans[cnt++]=sta[i];
tail=0;sta[tail++]=n-1;sta[tail++]=n-2;
for(int i=n-3;i>=0;--i){
while(tail>1&&!crossLeft(my[sta[tail-1]],my[sta[tail-2]],my[i]))tail--;
sta[tail++]=i;
}
for(int i=0;i<tail;++i)ans[cnt++]=sta[i];
}
int main(){
double x,y;
while(~scanf("%d%d",&n,&l)){
for(int i=0;i<n;++i){
scanf("%lf%lf",&x,&y);
my[i]=Point(x,y);
}
jarvis();
double re=4.0*acos(0.0)*l;
for(int i=0;i<cnt-1;++i)
re+=my[ans[i]].distance(my[ans[i+1]]);
printf("%.0f\n",re);
}
return 0;
}
利用极坐标,从点集中先找出一个最下方的点,这个点为凸包上的点,将所有的点 根据该点进行极角排序,并同时使用一个栈结构维护凸包上的点。按照极角序依次将点与栈顶的两个点做拐点判断:若右拐则将当前点加入栈中;否则,将栈顶的点弹出。当遍历完点集后,还在栈中的点就是凸包上的点,而且依次出栈可以得到从起点开始顺时针旋转的所有凸包上的点。
code:
#pragma warning(disable:4786)//使命名长度不受限制
#pragma comment(linker, "/STACK:102400000,102400000")//手工开栈
#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <stack>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d,&x,&y,&z)
#define rdl(x) scanf("%I64d,&x);
#define rds(x) scanf("%s",x)
#define rdc(x) scanf("%c",&x)
#define ll long long int
#define ull unsigned long long
#define maxn 1005
#define mod 1000000007
#define INF 0x3f3f3f3f //int 最大值
#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)
#define MT(x,i) memset(x,i,sizeof(x))
#define PI acos(-1.0)
#define E exp(1)
#define eps 1e-8
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll mul(ll a,ll b,ll p){ll sum=0;for(;b;a=(a+a)%p,b>>=1)if(b&1)sum=(sum+a)%p;return sum;}
inline void Scan(int &x) {
char c;while((c=getchar())<'0' || c>'9');x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
using namespace std;
int sgn(double x){
if(fabs(x)<0)return 0;
if(x<0)return -1;
return 1;
}
struct Point{
double x,y;
Point(){}
Point(double _x,double _y){
x=_x;
y=_y;
}
double distance(const Point &b)const{
return hypot(x-b.x,y-b.y);
}
}my[maxn];
int crossLeft(Point p0,Point p1,Point p2) {
return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
int sta[maxn],top;
bool cmp(Point a,Point b){
int tmp=crossLeft(my[0],a,b);
if(tmp>0)return 1;
if(!tmp&&my[0].distance(a)<my[0].distance(b))return 1;
return 0;
}
int n,l;
void graham(){
if(n==1){top=0;sta[0]=0;}
if(n==2){
top=1;
sta[0]=0;
sta[1]=1;
}
if(n>2){
for(int i=0;i<=1;++i)sta[i]=i;
top=1;
for(int i=2;i<n;++i){
while(top>0&&crossLeft(my[sta[top-1]],my[sta[top]],my[i])<=0)top--;
sta[++top]=i;
}
}
}
int main(){
while(~scanf("%d%d",&n,&l)){
Point tmp;
int k=0;
for(int i=0;i<n;++i){
double x,y;
scanf("%lf%lf",&x,&y);
my[i]=Point(x,y);
if(i!=0){
if((tmp.y>my[i].y)||((tmp.y==my[i].y)&&(tmp.x>my[i].x))){
tmp=my[i];
k=i;
}
}
else tmp=my[i];
}
my[k]=my[0];
my[0]=tmp;
sort(my+1,my+n,cmp);
graham();
double ans=2*PI*l;
for(int i=0;i<top;++i)ans+=my[sta[i]].distance(my[sta[i+1]]);
ans+=my[sta[0]].distance(my[sta[top]]);
printf("%.0f\n",ans);
}
return 0;
}