【二分】教主的花园解题报告

教主的花园

(p1.pas/cpp/in/out)

 

【问题背景】

LHX教主最近总困扰于前来膜拜他的人太多了,所以他给他的花园加上了一道屏障。 

 

【问题描述】

可以把教主的花园附近区域抽像成一个正方形网格组成的网络,每个网格都对应了一个坐标(均为整数,有可能为负),若两个网格(x1, y1)(x2, y2)|x1 – x2| + |y1 – y2| = 1,则说这两个网格是相邻的,否则不是相邻的。

教主在y = 0处整条直线上的网格设置了一道屏障,即所有坐标为(x, 0)的网格。当然,他还要解决他自己与内部人员的进出问题,这样教主设置了N个入口a1, a2, , aN可供进出,即对于y = 0上的所有网格,只有(a1, 0)(a2, 0)……,(aN, 0) 可以通过,之外的所有纵坐标为0的网格均不能通过,而对于(x, y)y不为0的网格可以认为是随意通过的。

现在教主想知道,给定M个点对(x1, y1)(x2, y2),并且这些点均不在屏障上,询问从一个点走到另一个点最短距离是多少,每次只能从一个格子走到相邻的格子。

 

【输入格式】

输入的第1行为一个正整数N,为屏障上入口的个数。

2行有N个整数,a1, a2, , aN,之间用空格隔开,为这N个入口的横坐标。

3行为一个正整数M,表示了M个询问。

接下来M行,每行4个整数x1, y1, x2, y2,有y1y2均不等于0,表示了一个询问从(x1, y1)(x2, y2)的最短路。

 

【输出格式】

输出共包含m行,第i行对于第i个询问输出从(x1, y1)(x2, y2)的最短路距离是多少。

 

【样例输入】

2

2 -1

2

0 1 0 -1

1 1 2 2

 

【样例输出】

4

2

 

【数据规模】

对于20%的数据,有nm10aixiyi绝对值不超过100

对于40%的数据,有nm100aixiyi绝对值不超过1000

对于60%的数据,有nm1000aixiyi绝对值不超过100000

对于100%的数据,有nm100000aixiyi绝对值不超过100000000

 

【考察点】

二分

【思路】

通过画图可知:所有可能的情况共3种:都在上面,都在下面,一上一下。

其中,目测可知:前两种情况可以直接横纵坐标分别相减,然后加起来即可(因为没有障碍)。后面一种稍微麻烦一点,通过画图分析:如果(x1,x2)区间上有通道,那么实际效果同前两种情况,如果没有,那么最短路一定是走最靠近(x1,x2)这个区间的某个通道。

从而问题转化成了找一个距离(x1,x2)最近的通道了,这里是坐标,很明显可以让那些通道有序,所以我们可以二分解决。

【提交情况】

1AC

【经验】

y1*y2<0来判断的总要中枪嘛……叫你秀高端……

【收获】

熟悉二分操作

 

ACCode:

Program p1;

Var

       n,m:longint;

       a:array[0..100010]oflongint;

       

Procedure terminate;

begin

       close(input);

       close(output);

       halt;

end;

 

Procedure qsort;  //排序,题目没说数据有序

 

   procedure sort(l,r: longint);

       var

              i,j,x,y:longint;

       begin

              i:=l;

              j:=r;

              x:=a[(l+r)div 2];

              repeat

              whilea[i]<x do

                     inc(i);

              whilex<a[j] do

                     dec(j);

              ifnot(i>j) then

              begin

                     y:=a[i];

                     a[i]:=a[j];

                     a[j]:=y;

                     inc(i);

                     j:=j-1;

              end;

       until i>j;

       

              if l<j then sort(l,j);

              if i<r then sort(i,r);

     end;

 

begin

       sort(1,n);

end;

 

Procedure init;

var

       i:longint;

begin

       assign(input,'p1.in');

       assign(output,'p1.out');

       reset(input);

       rewrite(output);

 

       readln(n);

       for i:=1 to n do

       begin

              read(a[i]);

       end;

       a[0]:=99999999;

       a[n+1]:=99999999;  //端点要注意,不然后面计算会把这里算上去,答案就诡异了……当然,也可以在后面特判,这种方法比较懒= =

       readln(m);

       

       qsort;

       

       //for i:=1 to n do

              //write(a[i],'');

       //writeln;

end;

       

Function divide(l,r,x:longint):longint;  //二分查找,找一个能把X插进去的地方

var

       m:longint;

begin

       whilel<r do

       begin

              m:=(l+r)>>1;

              if x<a[(l+r)>>1] then r:=m

              else l:=m+1;

   end;

       exit(m);

end;

 

Procedure main;

var

       wjmzbmr,x1,y1,x2,y2:longint;

       

       function solve:longint;

       var

              id1,id2,min:longint;

       begin

              id1:=divide(1,n,x1);  

              //write('id1:',id1,'');

              id2:=divide(1,n,x2);  //记录下两个x的位置

              //writeln('id2:',id2);

              if id1<>id2 then exit(abs(x1-x2)+abs(y1-y2)) //如果不相同,则代表a中有数位于(x1,x2)区间内

              else //否则就找附近的某个最优值,其实就是左右两个点就行了……不放心可以左右多枚举几个

              begin

                     min:=$7f7f7f7f;

                     if abs(y1-y2)+abs(x1-a[id1])+abs(x2-a[id1])<min then min:=abs(y1-y2)+abs(x1-a[id1])+abs(x2-a[id1]);

                     //writeln('当前:',abs(y1-y2)+abs(x1-a[id1])+abs(x2-a[id1]));

                     if abs(y1-y2)+abs(x1-a[id1+1])+abs(x2-a[id1+1])<min then min:=abs(y1-y2)+abs(x1-a[id1+1])+abs(x2-a[id1+1]);

                     //writeln('右:',abs(y1-y2)+abs(x1-a[id1+1])+abs(x2-a[id1+1]));

                    if abs(y1-y2)+abs(x1-a[id1-1])+abs(x2-a[id1-1])<min then min:=abs(y1-y2)+abs(x1-a[id1-1])+abs(x2-a[id1-1]);       

                     //writeln('左',abs(y1-y2)+abs(x1-a[id1-1])+abs(x2-a[id1-1]));

                     exit(min);

              end;

       end;

begin

       for wjmzbmr:=1 to m do

       begin

              readln(x1,y1,x2,y2);

              //坑爹

              if(y1=0)or(y2=0) then begin writeln('欧教坑爹啊!!'); terminate; end;  //输入数据不合法

              //同侧

              //if(y1*y2>0) then begin writeln(abs(x1-x2)+abs(y1-y2)); continue; end;

              If((y1<0) and (y2<0))or((y1>0)and(y2>0)) then begin writeln(abs(x1-x2)+abs(y1-y2));continue; end;  //千万注意不要y1*y2,会爆Longint

              //不同侧

              //if(y1*y2<0) then writeln(solve);

              if((y1>0) and (y2<0))or((y1<0)and(y2>0)) then writeln(solve);  //不同侧处理

       end;

end;

 

Begin

       init;

       main;

       terminate;

End.




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值