[uva]10135 - Herding Frosh

[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;
}



 

【心得】我的第一个凸包啊。好辛苦。迷迷糊糊居然写过了。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值