两条直线(蓝桥杯)二分枚举+RMQ

算法提高 两条直线  
时间限制:1.0s   内存限制:256.0MB
       
问题描述

给定平面上n个点。

求两条直线,这两条直线互相垂直,而且它们与x轴的夹角为45度,并且n个点中离这两条直线的曼哈顿距离的最大值最小。

两点之间的曼哈顿距离定义为横坐标的差的绝对值与纵坐标的差的绝对值之和,一个点到两条直线的曼哈顿距离是指该点到两条直线上的所有点的曼哈顿距离中的最小值。

输入格式

第一行包含一个数n。

接下来n行,每行包含两个整数,表示n个点的坐标(横纵坐标的绝对值小于109)。

输出格式
输出一个值,表示最小的最大曼哈顿距离的值,保留一位小数。
样例输入
4
1 0
0 1
2 1
1 2
样例输出
1.0
数据规模与约定

对于30%的数据,n<=100。

对于另外30%的数据,坐标范的绝对值小于100。

对于100%的数据,n<=105

分析:因为这两条直线是垂直的,为了处理方便把坐标系逆时针旋转45度,然后这两个直线就是垂直于坐标轴的,接着把坐标按照x坐标从小到大排序,然后二分答案,对于每个二分的答案mid,按照x坐标从左到右枚举,直到找到最大的xj满足xj-xi<=mid*2,在[i,j]区间内的点都在垂直线的范围内,剩下的[1,i-1]和[j+1,n]则属于水平线范围,如果满足在[1,i-1]和[j+1,n]找到最大y和最小y的差值<=mid*2则把答案向更优二分,否则增大mid的值。

求区间内的最大值和最小值可以用RMQ来求,因为此题比较特殊,可以分别从左,和从右遍历一边从两端来记录最大值和最小值

#include"stdio.h"

#include"string.h"

#include"math.h"

#define M 100009

#include"vector"

#include"queue"

#include"stdlib.h"

#include"deque"

#define eps 1e-3

#define PI acos(-1.0)

#define inf 10000000000000LL

#include"algorithm"

using namespace std;

struct node

{

    double x,y;

    bool operator<(const node &p)const

    {

        return x<p.x;

    }

}p[M];

int Log[M];

double maxl[M],minl[M],maxr[M],minr[M],val[M];

double dp_max[M][33],dp_min[M][33];

void initLog()

{

    Log[0]=-1;

    for(int i=1;i<M;i++)

    {

        Log[i]=(i&(i-1))==0?Log[i-1]+1:Log[i-1];

    }

}

void RMQ(int n)

{

    int m=Log[n];

    for(int i=1;i<=n;i++)

        dp_max[i][0]=dp_min[i][0]=p[i].y;

    for(int j=1;j<=m;j++)

    {

        for(int i=1;i<=n+1-(1<<j);i++)

        {

            dp_max[i][j]=max(dp_max[i][j-1],dp_max[i+(1<<(j-1))][j-1]);

            dp_min[i][j]=min(dp_min[i][j-1],dp_min[i+(1<<(j-1))][j-1]);

        }

    }

}

double lcp_max(int l,int r)

{

    int m=Log[r-l+1];

    return max(dp_max[l][m],dp_max[r+1-(1<<m)][m]);

}

double lcp_min(int l,int r)

{

    int m=Log[r-l+1];

    return min(dp_min[l][m],dp_min[r+1-(1<<m)][m]);

}

double max(double a,double b)

{

    return a>b?a:b;

}

double min(double a,double b)

{

    return a<b?a:b;

}

node ver(node a)

{

    double L=sqrt(a.x*a.x+a.y*a.y);

    double du=asin(a.y/L);

    du+=PI/4;

    a.x=L*cos(du);

    a.y=L*sin(du);

    return a;

}

//分别从两端记录最大值和最小值

void init(int n)

{

    maxl[1]=minl[1]=p[1].y;

    for(int i=2;i<=n;i++)

    {

        maxl[i]=max(p[i].y,maxl[i-1]);

        minl[i]=min(p[i].y,minl[i-1]);

    }

    maxr[n]=minr[n]=p[n].y;

    for(int i=n-1;i>=1;i--)

    {

        maxr[i]=max(p[i].y,maxr[i+1]);

        minr[i]=min(p[i].y,minr[i+1]);

    }

}

int judge(int n,double mid)

{

    int left=1,right=1;

    for(left=1;left<=n;left++)

    {

        double up=-inf;

        double down=inf;

        while(right<=n&&p[right].x-p[left].x<=mid*2)

        {

            right++;

        }

        if(left>1)

        {

            up=max(up,lcp_max(1,left-1));

            down=min(down,lcp_min(1,left-1));

        }

        if(right<=n)

        {

            up=max(up,lcp_max(right,n));

            down=min(down,lcp_min(right,n));

        }

        if(up-down<=mid*2)

            return 1;

    }

    return 0;

}

int main()

{

    int n;

    initLog();

    while(scanf("%d",&n)!=-1)

    {

        for(int i=1;i<=n;i++)

        {

            scanf("%lf%lf",&p[i].x,&p[i].y);

            p[i]=ver(p[i]);

        }

        sort(p+1,p+n+1);

        double l=0,r=max(p[n].x-p[1].x,maxl[n]-minl[n]);

        double mid;

        //init(n);

        RMQ(n);

        while(fabs(r-l)>eps)

        {

            mid=(l+r)/2;

            if(judge(n,mid))

            {

                r=mid;

            }

            else

            {

                l=mid;

            }

        }

        printf("%.1lf\n",r*sqrt(2.0));

    }

    return 0;

}

 

你可能感兴趣的:(蓝桥杯)