题目1471:Wall (tzcoder.cn)
题目大意:在一个坐标系里顺时针给出n个点,找出一个由其中某些点作为顶点组成的凸多边形,恰好能围住所有的N个点,以及还有一个以L为半径圆的周长。
这题用了Andrew算法求凸包。时间复杂度O(nlogn)。
步骤
1.对所有点进行排序,坐标x为主键,坐标y为副键。(第1、n点必在凸包上)
2.顺时针枚举点集,求下凸包(数学概念叉积解决,叉积为负,代表新点在直线右侧,否则在左侧)。用数组模拟栈维护当前在凸包上的点,新点入栈前,需要与栈顶两点比较,若新点在直线右侧或共线,旧点出栈。直到不能弹出,新点进栈。
3.逆序枚举点集,重复2操作。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+10;
const double pi=atan(1.0)*4;//pi的值
int n,l,top;
struct xx
{
double x,y;
}p[N],s[N];//p为点集,s为栈
bool cmp(xx a,xx b)//排序
{
if(a.x==b.x)
{
return a.y<b.y;
}
return a.x<b.x;
}
double cha(xx a,xx b,xx c)//算叉积
{
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
double jli(xx a,xx b)//算点与点之间的距离
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void andrew()
{
top=0;
sort(p+1,p+1+n,cmp);
for(int i=1;i<=n;i++)//下凸包
{
while(top>1&&cha(s[top-1],s[top],p[i])<=0)
{
top--;
}
s[++top]=p[i];
}
int t=top;
for(int i=n-1;i>=1;i--)//上凸包
{
while(top>t&&cha(s[top-1],s[top],p[i])<=0)
{
top--;
}
s[++top]=p[i];
}
}
int main()
{
while(~scanf("%d%d",&n,&l))
{
memset(p,0,sizeof(p));
memset(s,0,sizeof(s));
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
}
andrew();
double num=2*pi*l;
for(int i=1;i<top;i++)
{
num+=jli(s[i],s[i+1]);
}
printf("%.0f\n",num);
}
return 0;
}