[uva]10135 - Herding Frosh
【题目】http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1076
【题意】求最小多边形周长的值,要求原点在多边形上,多边形包含所有点。
【算法】枚举+凸包
【题解】将坐标按左下优先排序,然后删出重复点,按原点的极角[0-360)排序,删除在同一极角上的点(只保留最外面的)。然后枚举出所有原点出发,原点结束的凸包,比较得出最小值。
【细节】角度的浮点数判等。同一直线上,重点等等。
【测试】
100
4
1 1
-1 1
1 -1
-1 -1
2
-1 -1
1 1
2
1 1
2 2
5
1 1
1.1 1.1
2 2
3.3 3.3
4.4 4.4
11
1 1
-1 1
1 -1
-1 -1
0.1 1
0.3 0.5
-0.2 0.3
-0.3 -0.2
0.2 0.2
-0.1 -0.1
0.7 -0.35
ans:
10.83
7.66
7.66
14.45
10.83
【代码】
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define EPS (1E-10)
struct point{
double x,y;
}p[1010],h[1010];
double are_triangle(point a,point b,point c){ //三角形向量积,三点逆时针为正,顺时针为负,在同一直线上为0
return a.x*b.y+b.x*c.y+c.x*a.y-a.x*c.y-b.x*a.y-c.x*b.y;
}
double di(point a,point b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double pi=4.0*atan(1.0);
double J_angle(point a){ //求极角
if(a.y==0.0&&a.x>=0.0)return 0;
point ori,p1;
p1.y=ori.x=ori.y=0;
p1.x=1;
if(a.y>0)
return acos((di(a,ori)*di(a,ori)+1-di(a,p1)*di(a,p1))/(2*di(a,ori)));
return 2*pi-acos((di(a,ori)*di(a,ori)+1-di(a,p1)*di(a,p1))/(2*di(a,ori)));
}
int leftlower(point a,point b){ //按左下优先排序
if(a.x!=b.x)return a.x<b.x;
return a.y<b.y;
}
int small_angle(point a,point b){ //按极角小排
if(fabs(J_angle(a)-J_angle(b))>EPS)return J_angle(a)<J_angle(b);
return (a.x*a.x+a.y*a.y)<(b.x*b.x+b.y*b.y);
}
void copy_point(point *a,point *b){ //复制点
a->x=b->x;
a->y=b->y;
}
void sort_remove(int *n){ //删除重点
int i,oldn=*n,j;
sort(p,p+oldn,leftlower);
if(p[0].x==0&&p[0].y==0){
*n=*n-1;
j=0;
}
else j=1;
for(i=1;i<oldn;i++){
if((p[i].x==p[i-1].x&&p[i].y==p[i-1].y)||(p[i].x==0&&p[i].y==0))*n=*n-1;
else copy_point(&p[j++],&p[i]);
}
}
void remove_com(int *n){ //删除同一极角点,保留最外点
int old = *n,j=0,i;
for(i=1;i<old;i++){
if(fabs(J_angle(p[i])-J_angle(p[j]))<=EPS){
copy_point(&p[j],&p[i]);
*n=*n-1;
}
else copy_point(&p[++j],&p[i]);
}
}
double fun(int n){
int i,j,hn,top;
double min=-1,s;
point ori;
ori.x=ori.y=0;
sort_remove(&n);
if(n==0)return 0; //三个点以下特判
if(n==1)return 2.0*di(ori,p[0]);
if(n==2) return di(p[0],p[1])+di(ori,p[0])+di(ori,p[1]);
sort(p,p+n,small_angle);
remove_com(&n);
if(n==0)return 0;
if(n==1)return 2.0*di(ori,p[0]);
if(n==2) return di(p[0],p[1])+di(ori,p[0])+di(ori,p[1]);
for(i=0;i<n;i++){ //枚举
copy_point(&h[0],&ori); //开始的点
copy_point(&h[1],&p[i]); //枚举第一个连接原点的点
j=(i+1)%n;
top=1;
while(j!=i){
if(are_triangle(h[top-1],h[top],p[j])<0||(are_triangle(h[top-1],h[top],p[j])==0&&(p[j].x-h[top-1].x)*(p[j].x-h[top].x)>=0)){ //判断是不是凸的,这句话可以改。
top--;
if(top<=0)break;
}
else {
top++;
copy_point(&h[top],&p[j]);
j=(j+1)%n;
}
}
if(top<=0)continue; //不知道为什么会有负数
hn=top+1; //节点数+1,原点是第一个和最后一个
s=di(h[hn-1],ori);
for(j=1;j<hn;j++)s+=di(h[j],h[j-1]);
if(min==-1||min>s)min=s;
}
return min;
}
int main(){
int T,n,i,j=0;
scanf("%d",&T);
while(T-->0){
if(j==0)j=1;
else putchar('\n');
scanf("%d",&n);
for(i=0;i<n;i++)scanf("%lf%lf",&p[i].x,&p[i].y);
printf("%.2f\n",(double)((int)((fun(n)+2.005)*100))/100.0);
}
return 0;
}
【心得】我的第一个凸包啊。好辛苦。迷迷糊糊居然写过了。