【题意】给了n个点,求这n个点的凸包的边长之和再加上一个给定圆的周长!
【分析】第一次做凸包问题,通过这个题也了解了凸包是什么,好神奇啊,这里分享一个求凸包的方法的博客,个人认为写得很不错点击打开链接!~~~~
【解题思路】直接用求凸模板求最小凸包即可,这里最常用的可能就是上面博客里面提到的Andrew算法了(这里是水平序的)首先=按照x=从小到大排序(如果x相同,按照y排序),删除重复点后得到序列p1,p2,...,然后把p1,p2放入凸包中,从p3开始,当新点在凸包“前进”方向的左边时继续,否则依次删除最近加入凸包的点,直到新点在左边。
【复杂度】分析,这个算法在排序后仅仅是从左到右和从右到左各扫了一次,时间复杂度为O(n)。加上排序的时间复杂度为O(n*logn)!
【ps】这样一想,这个和斜率优化结合得很紧密啊!那里维护的上凸不就是一个凸包吗?
【AC代码】
#include <set>
#include <map>
#include <math.h>
#include <vector>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 10010;
const double PI = acos(-1);
struct Point{
int x,y;
Point(){}
Point(int x,int y):x(x),y(y){}
bool operator<(const Point &rhs)const{
if(x==rhs.x) return y<rhs.y;
return x<rhs.x;
}
}q[maxn],p[maxn];
int Cross(const Point &a,const Point &b){
return a.x*b.y-a.y*b.x;
}
double get_Dis(const Point &a,const Point &b){
return (double)sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int main(){
int T,n,r;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&r);
for(int i=0; i<n; i++) scanf("%d%d",&q[i].x,&q[i].y);
sort(q,q+n);
int m = 0;
for(int i=0; i<n; i++){//"下凸包"
while(m>1&&Cross(Point(p[m-1].x-p[m-2].x,p[m-1].y-p[m-2].y),Point(q[i].x-p[m-2].x,q[i].y-p[m-2].y))<=0) m--;
p[m++] = q[i];
}
int k = m;
for(int i=n-2; i>=0; i--){//"上凸包"
while(m>k&&Cross(Point(p[m-1].x-p[m-2].x,p[m-1].y-p[m-2].y),Point(q[i].x-p[m-2].x,q[i].y-p[m-2].y))<=0) m--;
p[m++] = q[i];
}
if(n>1) m--;
double sum = 0;
for(int i=0,j=1; i<m; i++,j++){
sum += get_Dis(p[i],p[j]);
}
sum += 2*PI*r;
printf("%.0f\n",sum);
if(T) puts("");
}
return 0;
}