2017普及第四题 跳房子 jump

来源:互联网 发布:java线程生成订单号 编辑:程序博客网 时间:2024/05/21 10:47

题目

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

跳房子的游戏规则如下:

在地面上确定一个起点,然后在起点右侧画 n 个格子,这些格子都在同一条直线上。每个格子内有一个数字( 整数),表示到达这个格子能得到的分数。玩家第一次从起点开始向右跳, 跳到起点右侧的一个格子内。第二次再从当前位置继续向右跳,依此类推。规则规定:

玩家每次都必须跳到当前位置右侧的一个格子内。玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和。

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

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

题解

因为数据很大,所以很容易想到二分答案。二分的每一次判定都是一次动态规划,方程如下:
f[i]=max(f[j])+w[i] (要求j点能跳到i点)
但是很显然超时,能得60分。
考试就想到了dp+二分,但是肯定会爆时间,不知道怎么优化。
dp+二分答案+一个优化的算法
观察动态方程,发现每一次都要找一个区间内的最大值来得出答案,这个区间满足能跳到i点,且f[i]单调递增,即如果后面的f[i]可行那么前面一定没有答案比它更优。
那么可以用单调队列来优化,也可以用堆。
单调队列+DP=绝配
那么问题来了——单调队列是什么?
翻了好几天的题解、百度百科,终于明白了。
单调队列是一个单调递增或递减的队列,队列先进先出,模板单调队列的区间内数量是固定的。简单来说,单调队列有两种操作:
1.把队头的多余数据去掉。在满足单调队列的题目中,如果一个点没法对当前的点贡献,则这个点对后面的点一定也是没有用的,这时候就要把这个点去掉,即头指针往后移一位
2.把一个点加进队列。每得出一个新的点都把它放进队列里,判断新点和队末点的大小,若新点优则把队末点去掉,即尾指针前移,继续这一步骤直至队末的点比新店优,这时把新点放在队尾
现在回到题目,发现一个f[i]得出后直接加进队列是错误的,因为f[i]只能跳到距离它d-g到d+g的点,距离它< d-g的点是转移不到的。
考虑维护一个now,表示当前最前面的一个没有加入单调队列的元素,当且仅当当前节点和它相差>=d-g的时候就让这个元素进队。这个进队在每次循环一个i的时候先做。
然后去除队头的多余状态
最后用队首的最大值得出f[i],并判断f[i]是否满足大于k分
时间复杂度(n log a[n])

代码

var  n,d,m,i,j,k,l,r,mid,ans:longint;  a,w:array[0..500000]of longint;  f:array[0..500000]of longint;  b,c:array[1..500000]of longint;function try(mid:longint):boolean;var  i,j,h,t,now,s,k:longint;begin  fillchar(f,sizeof(f),200);  s:=f[0];f[0]:=0;  h:=1;t:=0;now:=-1;  for i:=1 to n do    begin      for j:=now+1 to i-1 do        begin          if (a[j]+d-mid<=a[i])and(a[j]+d+mid>=a[i]) then            begin              while (t>=h)and(b[t]<=f[j]) do dec(t);              inc(t);b[t]:=f[j];c[t]:=a[j];              now:=j;            end;        end;      while ((c[h]+d+mid<a[i])or(c[h]+d-mid>a[i]))and(h<=t) do inc(h);      if (h<=t)and((c[h]+d+mid>=a[i])or(c[h]+d-mid<=a[i])) then f[i]:=b[h]+w[i];      if f[i]>=m then exit(true);    end;  exit(false);end;begin  readln(n,d,m);  for i:=1 to n do    begin      readln(a[i],w[i]);      if w[i]>0 then k:=k+w[i];      if k>=m then k:=m;    end;  if k<m then begin writeln(-1);halt;end;  l:=0;  r:=a[n];  while l<=r do    begin      mid:=(l+r) div 2;      if try(mid) then begin ans:=mid;r:=mid-1;end else l:=mid+1;    end;  writeln(ans);end.
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 保温饭盒摔了一下打不开怎么办 饭盒跟盖子盖一起打不开怎么办 玻璃杯子盖被水吸住打不开怎么办 电饭煲热剩饭没加水怎么办 微波炉碗盖子吸住了怎么办 微波炉转饭盖子吸住了怎么办 玻璃碗放进微波炉打不开怎么办 乐扣微波炉加热后打不开怎么办 美的微波炉盖子打不开怎么办 美的微波炉门都打不开了怎么办 饭煮好了有异味怎么办 一正常吃饭就胖怎么办 高铁盒饭没15的怎么办 上火车前票丢了怎么办 减肥期吃了汉堡怎么办 寿司店鳗鱼有刺怎么办 吃泡面胃难受该怎么办 吃上火的东西脸上长痘痘怎么办 减肥期间吃撑了怎么办 喝了变质的牛奶怎么办 绿豆糕吃多了会怎么办 小孩抓米饭烫了手怎么办 减肥不来月经了怎么办 吃了馊了的米饭怎么办 饭在冰箱里硬了怎么办 剩米饭反潮了怎么办 吃馊米饭中毒后怎么办? 蒸熟的米饭发黄怎么办 孕妇肉类吃的少怎么办 大米饭坏了吃了怎么办 米饭煮糊了锅怎么办 减肥吃了猪肉脯怎么办 吃了硬米饭胃痛怎么办 宝宝吃了硬物怎么办 米饭卡在喉咙里了怎么办 喉咙里卡了米饭怎么办 孕妇吃了坏鹅蛋怎么办 1岁大宝宝长短腿怎么办 行测中的判断推理怎么办 塑料盖子玻璃罐头瓶子打不开怎么办 猪肉烫火锅吃怎么办料