初次看到这道题,明显的动态规划。但是,朴素的转移方程的时间复杂度是O(n^2)的,对于n<=50000的程序根本无法承受。所以,我们要考虑优化,不谈优化到O(n),也要到O(nlogn)吧。怎么优化呢?
注意到这题有个隐含的条件:从每个栅栏往下走只能走到最多两个固定的栅栏上面:即向左拐、向右拐只能到确定的栅栏上,所以,利用线段树来维护是最好的选择。我们采用的就是线段树可以动态的更新每条线段的颜色!这样,就可以预处理出每条栅栏能够到达下面的哪条栅栏了。
具体做法:
1、线段树每条线段初始颜色pos赋为0.
2、依次询问每条线段左右端点所对应的点的颜色。插入该线段,并更改这条线段在线段树中所对应线段的颜色。
注意事项:
1、线段树中如果一条线段有多种颜色,那么将其pos值赋为-1
2、在更改pos值为-1的时候注意不要把以前插入的线段覆盖多了。比如,这个代码是错误的:
Procedure Insert_Tree(code,l,r:Longint);
var mid:Longint;
begin
if (Tree[code].l=l)and(Tree[code].r=r)then
begin
Tree[code].pos:=i;
exit;
end;
mid:=(tree[code].l+tree[code].r)>>1;
if mid>=r then Insert_Tree(code*2,l,r) else
if mid<=l then Insert_Tree(code*2+1,l,r) else
begin
Insert_Tree(code*2,l,mid);
Insert_Tree(code*2+1,mid,r);
end;
if (Tree[code*2].pos<>Tree[code*2+1].pos)or(Tree[code*2].pos=-1)or(Tree[code*2+1].pos=-1)then
Tree[code].pos:=-1 else Tree[code].pos:=Tree[code*2].pos;
end;
上面的代码会将不该全部覆盖的单色线段全部覆盖,而失去这个颜色在线段树中的记录。
具体的,多做一些线段树的题目就好了。
至于动态规划,很简单的转移方程,记忆化搜索的思想:
if f[i,0]+abs(b[i]-b[next[i,0]])<f[next[i,0],0] then f[next[i,0],0]:=f[i,0]+abs(b[i]-b[next[i,0]]);
if f[i,0]+abs(b[i]-e[next[i,0]])<f[next[i,0],1] then f[next[i,0],1]:=f[i,0]+abs(b[i]-e[next[i,0]]);
if f[i,1]+abs(e[i]-b[next[i,1]])<f[next[i,1],0] then f[next[i,1],0]:=f[i,1]+abs(e[i]-b[next[i,1]]);
if f[i,1]+abs(e[i]-e[next[i,1]])<f[next[i,1],1] then f[next[i,1],1]:=f[i,1]+abs(e[i]-e[next[i,1]]);
CODE
Program POJ2374;//By_Poetshy
Const
maxn=50000;
Type
rec=record
l,r,pos:Longint;
end;
Var
min,max,i,j,k,m,n,pos :Longint;
std :Longint;
b,e :Array[0..maxn]of Longint;
next :Array[0..maxn,0..1]of Longint;
f :Array[0..maxn,0..1]of Longint;
tree :Array[0..maxn*20]of rec;
Procedure Build_Tree(code,l,r:Longint);
var mid:Longint;
begin
Tree[code].l:=l;Tree[code].r:=r;
Tree[code].pos:=0;
if l+1>=r then exit;
mid:=(l+r)>>1;
Build_Tree(code*2,l,mid);
Build_Tree(code*2+1,mid,r);
end;
Procedure Insert_Tree(code,l,r:Longint);
var mid:Longint;
begin
if (Tree[code].l=l)and(Tree[code].r=r)then
begin
Tree[code].pos:=i;
exit;
end;
if Tree[code].pos>-1 then
begin
Tree[code*2].pos:=Tree[code].pos;
Tree[code*2+1].pos:=Tree[code].pos;
Tree[code].pos:=-1;
end;
mid:=(tree[code].l+tree[code].r)>>1;
if mid>=r then Insert_Tree(code*2,l,r) else
if mid<=l then Insert_Tree(code*2+1,l,r) else
begin
Insert_Tree(code*2,l,mid);
Insert_Tree(code*2+1,mid,r);
end;
end;
Function Get(code,p:Longint):Longint;
var mid:Longint;
begin
if Tree[code].pos<>-1 then exit(Tree[code].pos);
if Tree[code].l+1=Tree[code].r then exit(Tree[code].pos);
mid:=(Tree[code].l+Tree[code].r)>>1;
if mid>=p then exit(Get(code*2,p))else exit(Get(code*2+1,p));
end;
BEGIN
readln(n,pos);
min:=maxlongint;max:=-maxlongint;
for i:=n downto 1 do
begin
readln(b[i],e[i]);
if b[i]<min then min:=b[i];
if e[i]>max then max:=e[i];
end;
dec(pos,min-1);
for i:=1 to n do
begin
dec(b[i],min-1);
dec(e[i],min-1);
end;
Build_Tree(1,0,max-min+1);std:=-min+1;
for i:=n downto 1 do
begin
next[i,0]:=Get(1,b[i]);
next[i,1]:=Get(1,e[i]);
Insert_Tree(1,b[i],e[i]);
end;
fillchar(f,sizeof(f),127);
f[1,0]:=abs(pos-b[1]);f[1,1]:=abs(pos-e[1]);
b[0]:=std;e[0]:=std;
for i:=1 to n do
if f[i,0]<(maxlongint>>1)then
begin
if f[i,0]+abs(b[i]-b[next[i,0]])<f[next[i,0],0] then f[next[i,0],0]:=f[i,0]+abs(b[i]-b[next[i,0]]);
if f[i,0]+abs(b[i]-e[next[i,0]])<f[next[i,0],1] then f[next[i,0],1]:=f[i,0]+abs(b[i]-e[next[i,0]]);
if f[i,1]+abs(e[i]-b[next[i,1]])<f[next[i,1],0] then f[next[i,1],0]:=f[i,1]+abs(e[i]-b[next[i,1]]);
if f[i,1]+abs(e[i]-e[next[i,1]])<f[next[i,1],1] then f[next[i,1],1]:=f[i,1]+abs(e[i]-e[next[i,1]]);
end;
writeln(f[0,0]);
END.