题目描述
给定平面上n个点。
求两条直线,这两条直线互相垂直,而且它们与x轴的夹角为45度,并且n个点中离这两条直线的曼哈顿距离的最大值最小。
两点之间的曼哈顿距离定义为横坐标的差的绝对值与纵坐标的差的绝对值之和,一个点到两条直线的曼哈顿距离是指该点到两条直线上的所有点的曼哈顿距离中的最小值。
输入
第一行包含一个数n。
接下来n行,每行包含两个整数,表示n个点的坐标(横纵坐标的绝对值小于10^9)。
数据规模与约定
对于30%的数据,n< =100。
对于另外30%的数据,坐标范的绝对值小于100。
对于100%的数据,n< =10^5。
输出
输出一个值,表示最小的最大曼哈顿距离的值,保留一位小数。
样例输入复制
4 1 0 0 1 2 1 1 2
样例输出复制
1.0
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
const int N=100000;
struct P{int x,y;};
bool cmp(P a,P b){
if(a.x==b.x)return a.y<b.y;
return a.x<b.x;
}
P d[N+5];
struct F{int max,min;};
F fl[N+5],fr[N+5];
inline double Max(double a,double b){return a>b?a:b;}
inline double Min(double a,double b){return a>b?b:a;}
bool check(double m,int n){
m*=2;
int i,j=0;
for(i=0;i<n;i++){
while(j<n&&d[j].x-d[i].x<=m)j++; //找出最大的xj使xj-xi<=m,所以在xi到xj的这段区间的范围
是垂直线的范围之后找出[1,i-1]以及[j+1,n-1]范围内的最大的y和最小的y值,如果最大的y值减去最小的y值<=m,
则说明这是水平线的范围,则进行下一轮二分找出中点,找出更优解。
double MAX=-1e10;
double MIN=1e10;
if(j!=n){
MAX=Max(MAX,fr[j].max);
MIN=Min(MIN,fr[j].min);
}
if(i-1>=0){
MAX=Max(MAX,fl[i-1].max);
MIN=Min(MIN,fl[i-1].min);
}
// cout<<i<<" "<<j<<" "<<MAX<<" "<<MIN<<endl;
if(MAX-MIN<=m)return true;
}
return false;
}
void init(int n){
int i;
fl[0].min=fl[0].max=d[0].y;
for(i=1;i<n;i++){
fl[i].max=Max(fl[i-1].max,d[i].y); //fl[i]代表着前i个y坐标里面的最大值,
fl[i].min=Min(fl[i-1].min,d[i].y); //fl[i]代表着前i个y坐标里面的最小值。
}
fr[n-1].min=fr[n-1].max=d[n-1].y;
for(i=n-2;i>=0;i--){
fr[i].max=Max(fr[i+1].max,d[i].y); //fr[i]代表着后(n-i)个y坐标里面的最大值
fr[i].min=Min(fr[i+1].min,d[i].y); //fr[i]代表着后(n-i)个y坐标里面的最小值
}
}
int main(){
int i,n;
cin>>n;
for(i=0;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
d[i].x=x+y;
d[i].y=x-y; //将坐标轴按顺时针旋转45度。
}
sort(d,d+n,cmp); //按x坐标从小到大的顺序排序
init(n);
double l=0.0;
double r=1000000000;
while(r-l>=0.01){
double m=(l+r)/2; //二分法判断答案
// cout<<m<<endl;
if(check(m,n))r=m;
else l=m;
}
printf("%.1f\n",r); //分到不能再分之后 得出答案即为最小的最大的曼哈顿距离
return 0;
}