BZOJ 2829: 信用卡凸包 题解

这道题BZOJ有权限,用学校的号A的,所以没有传送门,直接上题目描述~

(我才不会说在vijos上有也有这道题,因为我没测试过,nanana

题目描述

这里写图片描述
这里写图片描述
输入样例

2
6.0 2.0 0.0
0.0 0.0 0.0
2.0 -2.0 1.5707963268

输出样例

21.66

提示

这里写图片描述
本样例中的2张信用卡的轮廓在上图中用实线标出,如果视1.5707963268为Pi/2(pi为圆周率),则其凸包的周长为16+4*sqrt(2)

限制
时间:10s 空间 128MB

解题分析

这道题很裸的凸包,但是有不少细节需要处理:

1.信用卡的长和宽是个问题

因为题目说的圆滑处理就是把四角磨圆,而不是额外增一圈,所以给出的信用卡的长和宽其实是有多出来的,需要砍去两个半径的长度。

2.旋转

还记得怎么旋转吗,其实我也不知道,^_^

3.一个圆

同经典题Wall一样,这道题也只需在凸包周长上再加一个圆的周长就行了。

4.精度问题

由于旋转涉及到三角函数,三角函数的大问题又是精度问题,所以建议用dcmp判断,不然快排,可能会让你开始怀疑 人生 C++

代码

#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,tot;
double a,b,R,ans,eps=1e-10;
int dcmp(double x)
{
  if (fabs(x)<eps) return 0;
  return (x<0)?-1:1;
}
struct data{
    double x,y;
    data (double x=0.0,double y=0.0):x(x),y(y){}
    bool operator < (const data b) const{
        return dcmp(x-b.x)<0||(dcmp(x-b.x)==0&&dcmp(y-b.y)<0);
    }
    bool operator == (const data b) const{
        return dcmp(x-b.x)==0&&dcmp(y-b.y)==0;
    }
}p[400005],ch[400005],s[4],c[400005];
data operator + (const data a,const data b) {return data(a.x+b.x,a.y+b.y);}
data operator - (const data a,const data b) {return data(a.x-b.x,a.y-b.y);}
double dot (const data a,const data b) {return a.x*b.x+a.y*b.y;}
double cross(const data a,const data b) {return a.x*b.y-a.y*b.x;}
double leng(const data a) {return sqrt(dot(a,a));}
data _Rotate(const data a,const double rad) {return data(a.x*cos(rad)-a.y*sin(rad),a.x*sin(rad)+a.y*cos(rad));}
//旋转向量
inline void readd(double &x) //读入优化
{
    x=0.0; int f=1; double t=1.0; char ch=getchar();
    while ('0'>ch||ch>'9'){if (ch=='-') f=-f; ch=getchar();}
    while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    if (ch=='.'){
        ch=getchar();
        while ('0'<=ch&&ch<='9') {t/=10.0; x+=(ch-'0')*t; ch=getchar();}
    }
    x*=f;
}
void _init()
{
    scanf("%d",&n); m=0;
    readd(b); readd(a); readd(R);
    a-=2*R; b-=2*R;
    s[0]=data(a/2,b/2); s[1]=data(a/2,-b/2); s[2]=data(-a/2,b/2); s[3]=data(-a/2,-b/2); //方便编写
    for (int i=1;i<=n;i++)
    {
        double x,y,z; readd(x); readd(y); readd(z);
        for (int j=0;j<4;j++) c[++m]=data(x,y)+_Rotate(s[j],z);
    }
}
void _work() //排序,由于本人强迫症,还去了个重。
{
    sort(c+1,c+m+1);
    int i=1,j; n=0;
    while (i<=m)
    {
        j=i+1; ch[++n]=c[i];
        while (j<=m&&c[i]==c[j]) j++;
        i=j;
    }
}
void _solve()
{
    _work(); tot=0;
    for (int i=1;i<=n;i++)
    {
        while (tot>1&&dcmp(cross(p[tot]-p[tot-1],ch[i]-p[tot-1]))<=0) tot--;
        p[++tot]=ch[i];
    }
    int k=tot;
    for (int i=n-1;i>0;i--)
    {
        while (tot>k&&dcmp(cross(p[tot]-p[tot-1],ch[i]-p[tot-1]))<=0) tot--;
        p[++tot]=ch[i];
    }
    ans=R*2*3.1415926;
    for (int i=1;i<tot;i++) ans+=leng(p[i+1]-p[i]);
    printf("%0.2lf",ans);
}
int main()
{
    _init();
    _solve();
    return 0;
}

PS:本博客过于简陋,再加上作者fhj水平过菜,如果各位神犇有建议,请多多指出,谢谢!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值