【跳棋、跳房子】与单调队列

【跳棋】


题目描述

小明迷恋上了一个新的跳棋游戏,游戏规则如下:棋盘是一排从0开始,顺序编号的格子, 游戏开始时你位于0号格子,你每次只能往编号大的格子跳,而且你每次至少需要跳过L个格 子,至多只能跳过R个格子。每个格子都有一个给定的伤害值,显然你希望得到的伤害值越 少越好。

你能告诉小明他当他跳到最后一个格子时受到的累积伤害值最小为多少吗? 如果无论如何小明都无法跳到最后一个格子,这个时候你需要输出”-1”。 注:i号格子跳过x个格子表示从i号格子跳到第i+x+1号格子。 

 

输入

第一行有三个整数nLR,n表示格子的编号从0nLR表示最 少需要跳过的格子数和最多能够跳过的格子数。

 

第二行有n个正整数,两个数字间用空格隔开,表示每个格子的伤害值。

 

输出

仅有一个整数,表示受到的最小伤害值,保证结果小于 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 

 

:这道题显而易见的是动态规划。转移方程也非常好推,我们把 f[i] 作为到第I格的最小伤害值,转移方程就是f[i]=min{f[j](j=i-r-1~i-l-1)}+a[i]

显而易见,动态规划的时间复杂度是On*n)的,肯定TLE,但是有没有一种算法可以快速枚举区间最值呢?

有三种方法:(1)优先队列(2)线段树(3)单调队列

而我们这次讲解最简易的单调队列解法,

单调队列最本质的本质就是用来求定区间最值的,用它也非常方便。

STD

var

  n,m,i,j,l,r,t,n1,v,x,w,head,tail:longint;

  q,a,f:array[-1000000..1000000] of int64;

begin

  readln(n,l,r);

  for i:=1 to n do

  begin

    read(a[i]);

    f[i]:=maxlongint;//防止溢出,设置无限大

  end;

  head:=1;//单调队列初始化为空

  for i:=1 to n do

  begin

    if (i-l-1)>=0 then//至少大于左边界

    begin

        while (head<=tail) and (f[q[tail]]>=f[i-l-1]) do

        dec(tail);//剔除不可能

        inc(tail);

        q[tail]:=i-l-1;

    end;

    while (head<=tail) and (q[head]<(i-r-1)) do inc(head);

    if head<=tail then f[i]:=a[i]+f[q[head]];//队列不为空

  end;

  if f[n]

  writeln(f[n]) else

  writeln(-1);//永远跳不到

End.

 

 

别看有两重循环,其实单调队列维护的复杂度只有O(n)!只有O(n)!只有O(n)!(重要的事说三遍)

 

    

 

 

 

 

                                                   【跳房子】


题目描述

跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。跳房子的游戏规则如下:

在地面上确定一个起点,然后在起点右侧画 n 个格子,这些格子都在同一条直线上。每个格子内有一个数字(整数),表示到达这个格子能得到的分数。玩家第一次从起点开始向右跳,跳到起点右侧的一个格子内。第二次再从当前位置继续向右跳,依此类推。规则规定:玩家每次都必须跳到当前位置右侧的一个格子内。玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和。

现在小 R 研发了一款弹跳机器人来参加这个游戏。但是这个机器人有一个非常严重的缺陷,它每次向右弹跳的距离只能为固定的 d。小 R 希望改进他的机器人,如果他花 g 个金币改进他的机器人,那么他的机器人灵活性就能增加 g,但是需要注意的是,每次弹跳的距离至少为 1。具体而言,当g < d时,他的机器人每次可以选择向右弹跳的距离为 d-g, d-g+1, d-g+2,…,d+g-2d+g-1d+g;否则(当g d时),他的机器人每次可以选择向右弹跳的距离为 123,…,d+g-2d+g-1d+g

现在小 R 希望获得至少 k 分,请问他至少要花多少金币来改造他的机器人。

输入

输入文件名为jump.in

第一行三个正整数 ndk,分别表示格子的数目,改进前机器人弹跳的固定距离,以及希望至少获得的分数。相邻两个数之间用一个空格隔开。

接下来 n 行,每行两个正整数

输出

输出文件名为jump.out

共一行,一个整数,表示至少要花多少金币来改造他的机器人。若无论如何他都无法获得至少 k 分,输出-1

样例输入

7 4 10

2 6

5 -3

10 3

11 -3

13 1

17 6

20 2

 

样例输出

2

 

提示

【输入输出样例 1 说明】

 

 花费 2 个金币改进后,小 R 的机器人依次选择的向右弹跳的距离分别为 235343,先后到达的位置分别为 2510131720,对应 1, 2, 3, 5, 6, 7 6 个格子。这些格子中的数字之和 15 即为小 R 获得的分数。

 

 

 

【数据规模与约定】

 

 

本题共 10 组测试数据,每组数据 10 分。对于全部的数据满足1 n 500000, 1 d 2000,1<=k<=1000

 

 

析:这道是普及组压轴的第四题,说真心话这是一道组合大水题,并不像我们想象的那么难。

我们把d-g, d-g+1, d-g+2,…,d+g-2d+g-1d+g看做成一个区间,f[i]就是伤害值,那不就等价于上一道题吗???

(于是这道题目的核心根本是定区间求最值+二分,二分枚举答案,用定区间求最小验证答案,就AC了。)

代码部分特别简单,把上一题的核心代码复制一遍+二分就行了。

伪代码STD

User:

Procedure binarysearch(l,r:longint);

Function check(x:longint):boolean;

{//上一题代码稍作修改

If 最小值=x then

  Exit(true) else

Exit(false);

}

Main(){

  Binarysearch(1,n);

}

你可能感兴趣的:(算法)