山头狙击战battle.pas

【问题描述】

Lucky为了掩护大部队,单枪匹马同敌人周旋,后来被敌人包围在某山头……等等,为什么怎么听怎么像狼牙山五壮士!不过不用着急,这次Lucky携带了足够的弹药,完全可以将涌上来的敌人一个一个干掉。Lucky是个神枪手,只要他的枪膛中有子弹,他就能将在他射程m(用从敌人位置到山头的直线距离算)以内的一个敌人瞬间射杀。但如果在射程内没有敌人,出于节约子弹考虑和面子问题,Lucky会等待敌人靠近然后射击。

正当Lucky为自己的强大而自我膨胀时,他忽然发现了一个致命的失误:他携带的枪是单发枪,每射出一发子弹都必须花k秒钟的时间装子弹。而凶残的敌人才不会花时间等你换子弹呢。他们始终在以1m/s的速度接近山头。而如果在一个敌人到达山头时Lucky无法将他击毙,那么我们可怜的Lucky就将牺牲在敌人的刺刀下。现在Lucky用心灵感应向你发出求助:要保住自己的性命并且歼灭所有敌人,Lucky最多只能用多少时间给枪装上一发子弹?

说明:假设一开始Lucky的枪中就有一发子弹,并且一旦确定一个装弹时间,Lucky始终会用这个时间完成子弹的装卸。希望你能帮助Lucky脱离险境。

【输入格式】battle.in

针对每组输入数据,第一行有两个整数n和m,(2≤n≤100,000; 1≤m≤10,000,000)n代表敌人个数,m代表Lucky的射程。

接下来有n行,每行一个整数mi,(1≤mi≤10,000,000),代表每个敌人一开始相对山头的距离(单位为米)。

【输出格式】battle.out

每组输出数据仅有一个整数,代表Lucky的换弹时间(单位为秒)。

【样例输入】

6 100

236

120

120

120

120

120

【样例输出】

25

【问题分析】

先来搞清一个问题:当装弹时间越短,可能消灭的敌人越多;装弹时间越长,可能消灭的敌人越少,这样随着装弹时间由短变长时,消来的敌人可能越来越少(也许不会变化,但至少不会增加的)。这样就可以满足一个递增变化的规律,我们可以先确定装弹时间的变化范围,然后使用二分这段时间来找到最合适的装弹时间。

(1)首先要对所有的敌人按从近到远进行排序,打击敌人也是按这个顺序打的;

(2)最短装弹时间显然为L=0(即无需等待,连发!),最长的第二近的敌人到达的时间(因为第一个无需装弹)R=a[2]。

(3)二分得装弹时间m,然后用这个装弹时间来检查所有的敌人是否能被消灭,若不能消灭表示装弹时间过长,则r :=m-1; 否则尝试用更长一点的装弹时间来检查是否能消灭敌人,即L := m + 1;

主要部分代码如下:

function check(x : longint) : boolean;
var
  i : longint;
  mark : boolean;
begin
  mark := true;
  s := 0;                    //用来表示射击第i个敌人时,已经消耗的时间
  for i := 1 to n do begin
    if d[i] - s > m then s := d[i] – m   //当第i个敌人还未移动到射程范围
    else if d[i] < s then begin           //若第i个敌人已经越过射手,则射手死
      mark := false; break;
    end;
    inc(s, x);
  end;
  chekc := mark;
end;
begin
  readln(n, m);
  for i := 1 to n do read(d[i]); readln;
  Qsort(1, n);
  L := 0; R := d[2];
  While L <= R do begin
    time :=(L + R) shr 1;
    If check(time) then L := time + 1 else R := time – 1;
  End;
  If not check(time) then dec(time);
  Writeln(time);
End.