题目描述
小明迷恋上了一个新的跳棋游戏,游戏规则如下:棋盘是一排从0开始,顺序编号的格子,游戏开始时你位于0号格子,你每次只能往编号大的格子跳,而且你每次至少需要跳过L个格子,至多只能跳过R个格子。每个格子都有一个给定的伤害值,显然你希望得到的伤害值越少越好。你能告诉小明他当他跳到最后一个格子时受到的累积伤害值最小为多少吗?如果无论如何小明都无法跳到最后一个格子,这个时候你需要输出”-1”。注:从i号格子跳过x个格子表示从i号格子跳到第i+x+1号格子。
输入
输入文件jump.in第一行有三个整数n、L和R,n表示格子的编号从0到n。L和R表示最少需要跳过的格子数和最多能够跳过的格子数。
第二行有n个正整数,两个数字间用空格隔开,表示每个格子的伤害值。
输出
输出文件jump.out仅有一个整数,表示受到的最小伤害值,保证结果小maxlongint。
样例输入
10 2 6
1 3 5 7 9 2 4 6 8 10
样例输出
12
数据范围限制
50%的数据,1 <= n <= 1000
65%的数据,1 <= n <= 10000
100%的数据,1 <= n <= 1000000,1 <= L <= R <= n
其中有15%的数据,1 <= n <= 1000000,1 <= L <= R <= 10
提示
思路:
大名鼎鼎的DP
状态转移方程:F[i]=A[i]+min(F[j]) (j=i-r-1~i-l-1)
直接暴力N*N搜肯定是不行的
So,可以用小根堆来维护一个最小值,就OK啦!
(其实是因为OJ跑得快,这样的数据带一个log N是过不了的,数据正解应该是用单调队列来维护)
代码:
var
f,a,q,d,o,p,g:array[0..1000000]of int64;
n,l,r,i,j,k,s,t,len,ls,x,y,z:longint;
procedure down(x:longint);
var
i,j,t:longint;
begin
i:=x;
while (i*2<=len)and(f[d[i*2]]<f[d[i]])or(i*2+1<=len)and(f[d[i*2+1]]<f[d[i]]) do
begin
j:=i*2;
if (f[d[j+1]]<f[d[j]])and(j+1<=len) then inc(j);
q[d[i]]:=j;
q[d[j]]:=i;
t:=d[i];
d[i]:=d[j];
d[j]:=t;
i:=j;
end;
end;
procedure up(x:longint);
var
i,j:longint;
begin
i:=x;
while (f[d[i div 2]]>f[d[i]])and(i>1) do
begin
q[d[i div 2]]:=i;
q[d[i]]:=i div 2;
x:=d[i div 2];
d[i div 2]:=d[i];
d[i]:=x;
i:=i div 2;
end;
end;
procedure charu(t:longint);
begin
d[len+1]:=t;
q[t]:=len+1;
inc(len);
up(len);
end;
procedure shanchu(x:longint);
var
i:longint;
begin
dec(len);
i:=q[x];
q[x]:=0;
q[d[len+1]]:=i;
d[i]:=d[len+1];
up(i);
down(i);
end;
begin
assign(input,'jump.in');
reset(input);
assign(output,'jump.out');
rewrite(output);
readln(n,l,r);
for i:=1 to n do
begin
read(a[i]);
f[i]:=maxlongint;
end;
for i:=l+1 to n do
begin
charu(i-l-1);
if i>r+1 then shanchu(i-r-2);
if len>0 then f[i]:=f[d[1]]+a[i];
end;
if f[n]<maxlongint then writeln(f[n])
else writeln(-1);
end.