我太菜了,写一发题解,代码改了好几天。。
十字形
【问题描述】
有一个平面直角坐标系,上面有一些线段,保证这些线段至少与一条坐标轴平行。我们需要计算出,这些线段构成的最大的十字形有多大。
称一个图形为大小为R的十字形,当且仅当,这个图形具有一个中心点,它存在于某一条线段上,并且由该点向上下左右延伸出的长度为R的线段都被已有的线段覆盖。
你可以假定:没有两条共线的段具公点,没有重合的线段。
【输入格式】
从文件 cross .in 中输入 数
第一行,个正整数 N,代表线段的数目。
以下 N行,每四个整数一行,每四个整数 x1,y1,x2,y2(x1=x2或 y1=y2,描述了一条线段。 )
【输出格式】
输出到文件 cross.out 中。
当不存在十字型时:输出一行“Human intelligence is really terrible”(不包括引号)。
否则:输出一行,一个整数为最大的 R。
【样例】
样例输入1:
1
0 0 0 1
样例输出1:
Human intelligence is really terrible
样例输入2:
>
3
-1 0 5 0
0 -1 0 1
2 -2 2 2
样例输出2:
2
【数据规模与约定】
对于 50% 的数据, N≤1000 。
对于 100% 的数据, 1≤N≤100000 ,所有坐标的范围在 −109到109 之间。
后 50% 内,所有数据均为随机生成。
【解题思路】
因为后 50% 的数据均为随机生成,所以本题各种乱搞都可以AC,然而我这个智障写了一个满的 n2 暴力。。这些乱搞的方法大概分两种,一种是把水平和竖直的线段分别按长度排序,暴力判交,如果当前线段的长度不大于当前答案长度的二倍,直接break。。
考虑稳定时间复杂度 nlogn 的做法。
首先把一个最小化的问题转化为判定性问题,如何判断是否存在一个大小大于等于x的十字型。这个问题就等价于,先把所有线段都从两端截掉x的长度(长度小于2x的线段直接删掉,等于2x的不能删),然后判断这些线段是否存在交点(有公共端点也算)。可以用set维护扫描线。
我们让一条横着的扫描线从下向上扫,每一条横坐标为x0的竖直直线就相当于两个事件——插入事件和删除事件,在这两个事件之间,扫描线上x0的位置有线段存在。每个水平的的线段可以看成是一个查询事件,查询扫描线在[x1,x2]区间上是否存在元素,若存在证明有交点。把所有事件按照y坐标从小到大排序即可。
【代码】
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
struct lin{int x1,y1,x2,y2;};//lin- line
vector<lin>ud,lr;//ud- up&down lr- left&right
//lr, ud 分别储存水平竖直线段
struct evnt{int op,l,r,t;};//event
//op==0 query [l,r]
//op==1 insert l (r=1) / erase l (r=-1)
bool cmp(evnt a,evnt b){
if(a.t!=b.t)return a.t<b.t;
return a.op*a.r>b.op*b.r;//先增,再查,最后删
}
bool ch(int x){ //ch- check 判断x是否可行
vector<evnt>ns;ns.clear();//ns记录所有事件
for(int i=0;i<ud.size();i++){//insert/erase event
int l=ud[i].y2-ud[i].y1;
if(2*x>l)continue;
ns.push_back((evnt){1,ud[i].x1,1,ud[i].y1+x});
//insert
ns.push_back((evnt){1,ud[i].x1,-1,ud[i].y2-x});
//erase
}
for(int i=0;i<lr.size();i++){//query event
int l=lr[i].x2-lr[i].x1;
if(2*x>l)continue;
ns.push_back((evnt)
{0,lr[i].x1+x,lr[i].x2-x,lr[i].y1});
}//check
sort(ns.begin(),ns.end(),cmp);//把询问按时间排序
set<int>exist;exist.clear();
for(int i=0;i<ns.size();i++){
if(ns[i].op==1)//insert/erase
if(ns[i].r==1){
exist.insert(ns[i].l);//insert
}else{
exist.erase(ns[i].l);//erase
}
else{//query
if(exist.lower_bound(ns[i].l)
!=exist.upper_bound(ns[i].r)){
return true;//找到交点
}
}
}
ns.clear();exist.clear();
return false;//没有交点
}
int main(){
freopen("cross.in","r",stdin);
freopen("cross.out","w",stdout);
int N;scanf("%d",&N);
for(int i=1;i<=N;i++){
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
if(y1>y2)swap(y1,y2);
if(x1>x2)swap(x1,x2);
if(x1==x2){//分别储存
ud.push_back((lin){x1,y1,x2,y2});
}else{
lr.push_back((lin){x1,y1,x2,y2});
}
}
if(!ch(1)){//连大小为1的十字形都没有,说明不存在十字形
printf("Human intelligence is really terrible\n");
return 0;
}
int l=1,r=1000000000;//[L,R]
while(r-l>1){//二分答案 直至区间长度为2
int mid=(l+r)/2;
if(ch(mid))l=mid;
else r=mid;
}
if(!ch(r))printf("%d\n",l);//优先把r作为答案
else printf("%d\n",r);
return 0;
}