2017 SWERC K.Blowing Candles(旋转卡壳)

As Jacques-Édouard really likes birthday cakes, he celebrateshis birthday every hour, instead of every year. His friendsordered him a round cake from a famous pastry shop, andplaced candles on its top surface. The number of candlesequals the age of Jacques-Édouard in hours. As a result,there is a huge amount of candles burning on the top of thecake. Jacques-Édouard wants to blow all the candles out inone single breath.You can think of the flames of the candles as being pointsin the same plane, all within a disk of radius R (in nanometers)centered at the origin. On that same plane, the airblown by Jacques-Édouard follows a trajectory that can bedescribed by a straight strip of width W, which comprisesthe area between two parallel lines at distance W, the linesthemselves being included in that area. What is the minimum width W such that Jacques-Édouardcan blow all the candles out if he chooses the best orientation to blow?

Input

The input file contains several test cases, each of them as described below.The first line consists of the integers N and R, separated with a space, where N is Jacques-Édouard’sage in hours. Then N lines follow, each of them consisting of the two integer coordinates xi and yi ofthe i-th candle in nanometers, separated with a space.

Limits

• 3 ≤ N ≤ 2 · 105;

• 10 ≤ R ≤ 2 · 108;

• for 1 ≤ i ≤ N, x2i + y2i ≤ R2;

• all points have distinct coordinates.


Output

For each test case, the output must follow the description below.Print the value W as a floating point number. An additive or multiplicative error of 10−5is tolerated:if y is the answer, any number either within [y − 10−5; y + 10−5] or within [(1 − 10−5)y; (1 + 10−5)y] isaccepted.Sample Input3 100 010 00 10Sample Output7.0710678118654755


题意:在半径为R的范围内有一些点,求能把他们包围住的矩形的最窄宽度。

思路:求凸包,再利用旋转卡壳,求出凸多边形的宽。先取凸包上一条边,在取一个点,直到这个点离这条边最远,然后顺时针枚举凸包上的边,这个点也会顺时针变化,这个操作复杂度为O(n)。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
//平面点的结构体模板
struct Point{  
    double x,y;  
    Point(){}  
    Point(double _x,double _y){ x=_x; y=_y; }  
    Point operator + (Point p){ return Point(x+p.x,y+p.y); }  
    Point operator - (Point p){ return Point(x-p.x,y-p.y); }  
    Point operator * (double d){ return Point(d*x,d*y); }  
    double dot(Point p){ return x*p.x+y*p.y; }  //内积  
    //外积,外积等于0则两点连线过原点或矢量平行,>0则连线斜率>45°,<0则连线斜率<45°
    double det(Point p){ return x*p.y-p.x*y; }  
}ps[200010];
//加上操作变成凸包模板
int n;
bool cmpxy(const Point &p,const Point &q){  //排序 
    if(p.x!=q.x) return p.x<q.x;  
    return p.y<q.y;  
}  
vector<Point> convex_hull(Point *ps,int n){   
    sort(ps,ps+n,cmpxy);  
    int k=0;  
    vector<Point> qs(n*2);    //构造中的凸包  
for(int i=0;i<n;++i){    //构造凸包的下侧 
        //相邻边的叉积大于0 
        while(k>1 && (qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0)--k;
        qs[k++]=ps[i];  
    }  
for(int i=n-2,t=k;i>=0;--i){ //构造凸包的上侧  
        //相邻边的叉积小于0
        while(k>t && (qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0)--k;
        qs[k++]=ps[i];  
    }  
    qs.resize(k-1);  //去掉重复的左下角点
    return qs;  
}  
double dist(Point p,Point q){  //两点距离平方 
    return (p-q).dot(p-q);  
}
int main()
{
    int n,r;
    while(~scanf("%d%d",&n,&r)&&n)
    {
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&ps[i].x,&ps[i].y);
        }
        vector<Point> qs=convex_hull(ps,n);
        int p=1;
        double len;
        int sz=qs.size();
        if(sz==2)
        {
            printf("0.000000000\n");
            continue;
        }
        double minofmaxh=2.0*r;
        qs[sz]=qs[0];
        for(int i=0;i<sz;i++)
        {
            len=sqrt(dist(qs[i],qs[i+1]));
            while(true)
            {
                double h=fabs((qs[i]-qs[i+1]).det(qs[p]-qs[i+1]))/len;
                if(p<sz-1)
                {
                    double h1=fabs((qs[i]-qs[i+1]).det(qs[p+1]-qs[i+1]))/len;
                    if(h1>=h)
                    {
                        p++;
                    }
                    else
                    {
                        minofmaxh=min(minofmaxh,h);
                        break;
                    }
                }
                else
                {
                    double h1=fabs((qs[i]-qs[i+1]).det(qs[0]-qs[i+1]))/len;
                    if(h1>=h)
                    {
                        p=0;
                    }
                    else
                    {
                        minofmaxh=min(minofmaxh,h);
                        break;
                    }
                }
            }
        }
        printf("%.10lf\n",minofmaxh);
    }
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值