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.
- 2017普及第四题 跳房子 jump
- NOIP 2017 普及组 跳房子 jump
- [NOIP2017普及组]跳房子
- ◆竞赛题目◆◇NOIP 2017◇ jump 跳房子
- NOIP2017普及组★跳房子
- NOIP2017跳房子(普及T4)
- 【NOIP2017普及组正式赛】跳房子
- noip2011普及组第四题
- noip2015普及组第四题
- NOIP2005普及组第四题
- 跳房子
- NOIP2015普及组第四题解题报告
- NOIP2016普及组 第四题 题解
- 循环(NOIP2005普及组第四题)
- LeetCode 第四十五题(Jump Game II)Java
- Noip2015普及组第四题 Salesman的解题报告
- 【noip普及组2016】第四题“魔法阵”
- NOIP2016普及组第四题——魔法阵
- scala学习笔记-集合
- 34. Search for a Range
- 不得不说的Android6.0新特性
- 【特征检测】HOG特征算法
- 全方位落后,日本的AI人才、研究、专利和投资均被中国甩开
- 2017普及第四题 跳房子 jump
- 机房收费系统之查询余额
- Windows停止进程
- Electron-vue环境配置,打包报错问题解决
- java引入泛型的好处
- SLAM入门资料汇总
- Centos7中安装jdk1.8
- theano.scan
- 查询select中选中的值bootstrao+Vue.js+spring