邪恶的大叔(push)

算法:DP
 
分析:这道题的题目内容实在是太XE了,大叔推倒LOLI,千百年不变的真理啊……(原题来自于SPOJ_4197)
      我们可以预处理出推倒某只LOLI的左边界和右边界,还要注意有可能推倒的不一定是最边上的LOLI,也可以是中间的某只LOLI,因此还要处理一下。
      由此可得转移方程:
       f[i]:=min(f[i],f[last[i]-1]+1);
       f[i]:=min(f[i],f[left[i]-1]+1);
       f[right[i]]:=min(f[right[i]],f[i-1]+1);

      注:推倒确实是多米诺形式的。

program push;



const

 maxn=100000;



type

 atp=record

  x,h:longint;

 end;



var

 n:longint;

 a:array [0..maxn] of atp;

 last,f,left,right:array [0..maxn] of longint;

 

procedure init;

var

 i:longint;

begin

 readln(n);

 for i:=1 to n do readln(a[i].x,a[i].h);

end;

 

function min(x,y:longint):longint;

begin

 if x0) and (a[i].x-a[j].x<=a[i].h) do

      begin

       left[i]:=left[j];

       j:=left[j]-1;

      end;

    end

   else left[i]:=i;

  end;

  

 for i:=n-1 downto 1 do

  begin

   {求能到达的最右范围且能保证这个最右范围一定是连续的。}

   if a[i+1].x-a[i].x<=a[i].h then

    begin

     right[i]:=right[i+1];

     j:=right[i]+1;

     while (j<=n) and (a[j].x-a[i].x<=a[i].h) do

      begin

       right[i]:=right[j];

       j:=right[j]+1;

      end;

    end

   else right[i]:=i;

  end;

  

 i:=1;

 while i<=n do

  begin

   for j:=i to right[i] do last[j]:=i;

   i:=right[i]+1;

  end;

  

end;



procedure main;

var

 i:longint;

begin

 fillchar(f,sizeof(f),10);

 f[0]:=0;

 for i:=1 to n do

  begin

   f[i]:=min(f[i],f[last[i]-1]+1);

   f[i]:=min(f[i],f[left[i]-1]+1);

   f[right[i]]:=min(f[right[i]],f[i-1]+1);

  end;

end;



begin

 assign(input,'push.in'); reset(input);

 assign(output,'push.out'); rewrite(output);



 init;

 ycl;

 main;

 writeln(f[n]);



 close(input); close(output);

end.




你可能感兴趣的:(总结)