[NOIP2017 普及组]跳房子 【题解】
题目背景
NOIP2017 普及组 T4
题目描述
跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。
跳房子的游戏规则如下:
在地面上确定一个起点,然后在起点右侧画 \(n\) 个格子,这些格子都在同一条直线上。每个格子内有一个数字(整数),表示到达这个 格子能得到的分数。玩家第一次从起点开始向右跳,跳到起点右侧的一个格子内。第二次再从当前位置继续向右跳,依此类推。规则规定:
玩家每次都必须跳到当前位置右侧的一个格子内。玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和。
现在小 R 研发了一款弹跳机器人来参加这个游戏。但是这个机器人有一个非常严重的缺陷,它每次向右弹跳的距离只能为固定的 \(d\)。小 R 希望改进他的机器人,如果他花 \(g\) 个金币改进他的机器人,那么他的机器人灵活性就能增加 \(g\),但是需要注意的是,每 次弹跳的距离至少为 \(1\)。具体而言,当 \(g<d\) 时,他的机器人每次可以选择向右弹跳的距离为 \(d-g,d-g+1,d-g+2,\ldots,d+g-1,d+g\);否则当 \(g \geq d\) 时,他的机器人每次可以选择向右弹跳的距离为 \(1,2,3,\ldots,d+g-1,d+g\)。
现在小 R 希望获得至少 \(k\) 分,请问他至少要花多少金币来改造他的机器人。
输入格式
第一行三个正整数 \(n,d,k\) ,分别表示格子的数目,改进前机器人弹跳的固定距离,以及希望至少获得的分数。相邻两个数 之间用一个空格隔开。
接下来 \(n\) 行,每行两个整数 \(x_i,s_i\) ,分别表示起点到第 \(i\) 个格子的距离以及第 \(i\) 个格子的分数。两个数之间用一个空格隔开。保证 \(x_i\) 按递增顺序输入。
输出格式
共一行,一个整数,表示至少要花多少金币来改造他的机器人。若无论如何他都无法获得至少 \(k\) 分,输出 \(-1\)。
样例 #1
样例输入 #1
7 4 10
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2
样例输出 #1
2
样例 #2
样例输入 #2
7 4 20
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2
样例输出 #2
-1
提示
输入输出样例 1 说明
花费 \(2\) 个金币改进后,小 R 的机器人依次选择的向右弹跳的距离分别为 $ 2, 3, 5, 3, 4,3$,先后到达的位置分别为 \(2, 5, 10, 13, 17, 20\),对应$ 1, 2, 3, 5, 6, 7$ 这 \(6\) 个格子。这些格子中的数字之和 $ 15$ 即为小 R 获得的分数。
输入输出样例 2 说明
由于样例中 \(7\) 个格子组合的最大可能数字之和只有 \(18\),所以无论如何都无法获得 \(20\) 分。
数据规模与约定
本题共 10 组测试数据,每组数据等分。
对于全部的数据满足\(1 \le n \le 5\times10^5\),\(1 \le d \le2\times10^3\),\(1 \le x_i, k \le 10^9\),\(|s_i| < 10^5\)。
对于第 \(1, 2\) 组测试数据,保证 \(n\le 10\)。
对于第 \(3, 4, 5\) 组测试数据,保证 \(n \le 500\)。
对于第 \(6, 7, 8\) 组测试数据,保证 \(d = 1\)。
浅浅地分析下?
\(n \le 10\)
首先,如果是我去考试,这是第四题啊!这说明这什么,这题是提高组难度的!
于是乎,我会去骗分
我一看,那个\(-1\)的应该是最好骗的吧,直接把所有正数加起来,如果小于\(k\),那就输出\(-1\)咯
我的眼睛渐渐地移到了\(n \le 10\)上,贪婪的目光似要把这\(10\)分硬生生的拿chi 下diao
我的念头从正数移到了……暴搜上!
枚举所有可能的答案,在判断这个方案是否能达到\(k\),是就输出啦
那个……怎么判断这个方案怎么达到\(k\)?
爆枚呗!每个点最多有\(n\)个决策,一一枚举再记忆化一下应该可以拿下这10分吧?(我没试过)
\(n \le 500\)
刚刚我们在分析枚举所有可能的答案时,我们是\(for\)循环一个一个的枚举
有没有更优的枚举方法?
有!因为这一段格子是连续的,我们很容易想到了二分答案!
定义两个指针,\(l\)和\(r\)
- \[mid=(l+r)/2=(l+r)>>1
\] 如果这个答案可以行(\(check()\)函数),由于题目要求的是最小,所以答案可能在左边,\(r=mid\),否则答案在右边\(l=mid+1\)
这里为什么是\(r=mid\)而不是\(r=mid-1\)? 因为我们输出的是\(r\)
现在的重点是怎么写\(check()\)函数?
我们刚刚的思路是暴力枚举,再思考一下?
线性的?每个点最多有n个决策?达到\(k\)?这怎么看都想动态规划啊
于是我们来设计状态,\(dp_i\)表示跳到了第\(i\)个格子所得到的最大分数
\]
这里,就有同学有疑问了(应该只有我)有没有可能往前跳?答案是不可能的
想想,如果有三个点从前到后分别是\(1,2,3\)号,往前跳指的是从\(1\)跳到\(3\),然后跳到\(2\),那为何不直接一次跳过去呢?所以只需要往后转移
时间复杂度为\(O(n^2log_2MAX(x_i))\)
这样就可以过\(n \le 500\)的点了
\(Code\)
#include<bits/stdc++.h> using namespace std;
#define INF 1e9
struct node{
int pos=0,value=0;
}a[500005];
int n,d,k;
int dp[500005];
bool check(int minn,int maxx){
dp[0]=0LL;
for(int i=1;i<=n;i++){
dp[i]=-INF;
for(int j=0;j<i;j++)
if(a[i].pos-a[j].pos>=minn&&a[i].pos-a[j].pos<=maxx)
dp[i]=max(dp[i],dp[j]+a[i].value);
if(dp[i]>=k) return true;
}
return false;
}
int main()
{
cin>>n>>d>>k;
for(int i=1;i<=n;i++) cin>>a[i].pos>>a[i].value;
int l=0,r=INF;
while(l<r){
int mid=(l+r)>>1;
if(check(max(1,d-mid),d+mid)) r=mid;
else l=mid+1;
}
cout<<(r==INF?-1:r);
return 0;
}
\(n \le 5*10^5\)
我们考虑在50分的做法上继续优化
我们看这个复杂度\(O(n^2log_2MAX(x_i))\),最大的麻烦是什么?
那肯定是\(n^2\)啊,有没有办法把\(n^2\)优化成\(nlog_2n\)或\(n\)呢?
我们观察\(check()\)函数,发现\(dp\)的转移都是在它之前满足条件的,动态变化的,很自然的想到了单调队列
所以我们只需要维护\(deque\)就行了
怎么维护呢?
定义一个指针\(now\),让他指向第一个可行的点
while(a[i].pos-a[now].pos>maxx&&now<i) now++;
- 然后扫一遍可行的区间
while(a[i].pos-a[now].pos<=maxx&&a[i].pos-a[now].pos>=minn&&now<i){
- 更新一下这个点,讲不可行的点从队尾弹出,不可行包括比\(dp_{now}\)小或者超出了区间
while(!q.empty()&&(dp[q.back()]<=dp[now]||a[i].pos-a[q.back()].pos>maxx)) q.pop_back();
- 将新的点从队尾\(push\),同时,指针枚举下一个点
q.push_back(now),now++;
- 最后在\(while\)外,将队首不可行的方案弹出
while(!q.empty()&&a[i].pos-a[q.front()].pos>maxx) q.pop_front();
- 最后判断和转移
if(!q.empty()) dp[i]=dp[q.front()]+a[i].value;
if(dp[i]>=k) return true;
注意,\(now\)千万不要重新赋值,这样才能保证每个点进出一遍
时间复杂度为\(O(nlog_2MAX(x_i))\)
记得开\(long\quad long\)哦,然后你就可以\(AC\)啦
\(Code\)
#include<bits/stdc++.h>
using namespace std;
#define INF 1e18
typedef long long ll;
struct node{
ll pos=0,value=0;
}a[500005];
ll n,d,k;
ll dp[500005];
bool check(ll minn,ll maxx){
for(int i=1;i<=n;i++) dp[i]=-INF;
dp[0]=0LL;
deque<int> q;
int now=0;
for(ll i=1;i<=n;i++){
while(a[i].pos-a[now].pos>maxx&&now<i) now++;
while(a[i].pos-a[now].pos<=maxx&&a[i].pos-a[now].pos>=minn&&now<i){
while(!q.empty()&&(dp[q.back()]<=dp[now]||a[i].pos-a[q.back()].pos>maxx)) q.pop_back();
q.push_back(now);
now++;
}
while(!q.empty()&&a[i].pos-a[q.front()].pos>maxx) q.pop_front();
if(!q.empty()) dp[i]=dp[q.front()]+a[i].value;
if(dp[i]>=k) return true;
}
return false;
}
int main()
{
cin>>n>>d>>k;
for(ll i=1;i<=n;i++) cin>>a[i].pos>>a[i].value;
ll l=1,r=a[n].pos;
while(l<r){
ll mid=(l+r)>>1;
if(check(max((long long)1,d-mid),d+mid)) r=mid;
else l=mid+1;
}
cout<<(r==INF?-1:r);
return 0;
}
[NOIP2017 普及组]跳房子 【题解】的更多相关文章
- [NOIP2017普及组]跳房子(二分,单调队列优化dp)
[NOIP2017普及组]跳房子 题目描述 跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一. 跳房子的游戏规则如下: 在地面上确定一个起点,然后在起点右侧画 nn 个格子, ...
- NOIP2017普及组T2题解
还是神奇的链接 上面依然是题目. 这道题依然很简单,比起2015年的普及组t2好像还是更水一些. 不过这道题能讲的比第一题多. 我们一起来看一下吧! 这一题,我们首先将书的编号全部读入,存在一个数组里 ...
- NOIP2017普及组T1题解
神奇的链接 上面时题目. 其实不得不说,这一题很水,比2015年的第一题水多了. 直接按题目套公式就行了,当然你也可以像我一样化简一下. 直接看代码: #include<cstdio> # ...
- Luogu 3957 [NOIP2017]普及组 跳房子
写了好久,感觉自己好菜,唉…… 首先发现这个$g$的取值具有单调性,可以想到二分答案,然后考虑用$dp$来检验,这样子可以写出朴素的转移方程: 设$f_i$表示以$i$结尾的最大价值,那么有$f_i ...
- 【题解】NOIP2017 提高组 简要题解
[题解]NOIP2017 提高组 简要题解 小凯的疑惑(数论) 不讲 时间复杂度 大力模拟 奶酪 并查集模板题 宝藏 最优解一定存在一种构造方法是按照深度一步步生成所有的联通性. 枚举一个根,随后设\ ...
- 「LOJ 6373」NOIP2017 普及组题目大融合
NOIP2017 普及组题目大融合 每个读者需要有某个后缀的书,可以暴力map,复杂度\(o(9*nlog(n))\),也可以反串建trie树,复杂度\(o(9*n)\). 故可以求出需要的最少的RM ...
- P3956 [NOIP2017 普及组] 棋盘
P3956 [NOIP2017 普及组] 棋盘 题目 题目描述 有一个 m×m 的棋盘,棋盘上每一个格子可能是红色.黄色或没有任何颜色的.你现在要从棋盘的最左上角走到棋盘的最右下角. 任何一个时刻,你 ...
- noip2017普及组
过了这么久才来写博客,也是我这么一段时间都很低迷吧.... 老实来说,今年应该是要打提高组的...可还是打了普及组... 其实最猥琐的还是我连普及都写挂了,作为一个学了两年的人,图论,进阶dp都写过的 ...
- NOIP2017普及组比赛总结
期中考总结&NOIP2017总结 2017年11月11日,我第二次参加NOIP普及组复赛.上一年,我的得分是250分,只拿到了二等奖.我便把目标定为拿到一等奖,考到300分以上. 早上8点多, ...
随机推荐
- 成功解决:snippet设置的开机自启没有效果
1.问题描述 勾选开机启动后.没有效果.每次开机都要我重新找到对应的安装目录.双击运行开启 2.解决方法 将snipaste的快捷方式放到开机启动目录下 C:\Users\Administrator\ ...
- vs 自定义代码块
代码自动生成,让代码飞~ 我目前用的是vs2013,c,c++用的多,vs自带了一套代码块规则,下面我们就以for举例子. 1 原生代码块怎么使用 输入for后按Tab键: 这时候可以修改 size_ ...
- 【云原生 · Kubernetes】Kubernetes运维
(1)Node的隔离与恢复 在硬件升级.硬件维护等情况下,需要将某些Node隔离.使用kubectl cordon <node_name>命令可禁止Pod调度到该节点上,在其上运行的Pod ...
- mysql 在连接表中的要点
思路:分析需求,分析字段来自哪些表 (连接查询) 确定使用哪种连接查询? 确定交叉点(这两个表中哪些数据是相同的) 判断条件 such as 学生表中的 ...
- 关于linux mint(nemo)添加右键菜单修改方法
前言 其实在 linux mint 桌面上右键弹出的菜单,以及在资源管理器 nemo 中右键菜单,这些都是基于 nemo,进行的操作,所以更改右键菜单也就是更改nemo的配置文件 操作 在目录 /ho ...
- Vue2基本组件间通信
Vue2组件通信的基础方式 自己的理解:组件化通信,无非就是数据你传我,我传你,两个组件的相互交流,方法很多,下方有图示(此篇建议小白阅读,大神的话也不会看,哈哈哈哈!仅供参考,有不同的意见可以一起交 ...
- Oracle plsql Database links
在多系统对接的过程中,子系统要用到的基础数据例如部门和用户名是要和门户OA系统保持一致的,这个哦每天都要更新同步一次, 在Oracle中,存储过程可以完美的简单的解决这一问题.把目标数据库在plsql ...
- jmeter 从多个数中随机取一个值的方法
问题描述:使用jmeter进行接口测试时,遇到枚举值(如:10代表闲置.15代表使用中.20代表维修等)我们需要随机取一个类型传到接口中. 解决思路:通过函数助手查找随机函数,找到__chooseRa ...
- 【每日一题】【优先队列、迭代器、lambda表达式】2022年1月15日-NC119 最小的K个数
描述 给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数.例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可). 数据范围: ...
- 这玩意也太猛了!朋友们,我在此严正呼吁大家:端好饭碗,谨防 AI!
你好呀,我是歪歪. 最近几天大火的 ChatGPT 你玩了吗? 如果你不知道它是个什么东西,那么我让它给你来个自我介绍: 说白了,就是一个可以对话的人工智能. 我开始以为就是一个升级版的"小 ...