题目描述
小明迷恋上了一个新的跳棋游戏,游戏规则如下:棋盘是一排从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
提示
TJ
这道题很明显是动态规划。
设f[i]表示到达i号点受到的最小伤害。可以很容易地推出动态转移方程:
f[i]=min(f[i-r-1~i-l-1]+a[i]);
时间复杂度O((n-l)*(r-l)),因数据比较水,能得80分。
现在问题出现了,如何优化时间?
方程中的f[i-r-1~i-l-1]原本需要枚举,我们能不能用O(1)的时间找出其中的最小值?有三种方法:堆维护,单调队列或线段树。下面附上堆维护的标程。
BC
var
f,a,q,d:array[0..1000000]of int64;
len,n,l,r,i,j,k,s,t:longint;
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 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 insert(t:longint);
begin
d[len+1]:=t;
q[t]:=len+1;
inc(len);
up(len);
end;
procedure delete(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;
f[0]:=0;
for i:=l+1 to n do
begin
insert(i-l-1);
if i>r+1 then delete(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);
close(input);close(output);
end.