Online JudgeLuogu P1052

Label:Dp,思维题,缩点,数学

题目描述

在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:$0,1,..,L$(其中$L$是桥的长度)。坐标为$0$的点表示桥的起点,坐标为$L$的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是$S$到$T$之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为$L$的点时,就算青蛙已经跳出了独木桥。

题目给出独木桥的长度$L$,青蛙跳跃的距离范围$S,T$,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。

输入

第一行有1个正整数L \((1<=L<=10^9)\),表示独木桥的长度。

第二行有3个正整数$S,T,M$,分别表示青蛙一次跳跃的最小距离,最大距离及桥上石子的个数。

第三行有$M$个不同的正整数分别表示这$M$个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。

石子的位置不一定是从小到大的。

输出

一个整数,表示青蛙过河最少需要踩到的石子数。

样例

Input#1

10
2 3 5
2 3 5 6 7

Output#1

2

Input#2

3000000
5 7 99
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 80 81 82 83 84 85 86 87 88 89 100 101 102 103 104 105 106 107 108 109 135 136 137 138 139 140 141 142 143 144 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 205 206 207 208 209 210 211 212 213 214 245 246 247 248 249 250 251 252 253 254

Output#2

12

Hint

这里的数据范围跟原题有出入。

对于30%数据,\(L<=10^4\)。

对于70%数据,\(L<=10^7\)。

对于100%数据,\(L<=10^9\),\(m<=100\),$1<=s,t<=300$

题解

30pts

\(O(LT)\)。用$dp[i]\(表示跳到位置i需要踩的最少石头数目。枚举位置\)[0,L+T]\((上界注意是L+T),再枚举前面的\)[S,T]$个位置,找最小值转移。

70pts

\(O(L+T)\)。优化找最小值的部分,很容易想到用单调队列搞一下,每次转移变成$O(1)$。

namespace p70{
int head=1,tail=0,q[350];
void solve(){
q[++tail]=0;
dp[0]=mark[0];
int ans=666;
for(int i=1;i<=n+T;i++){
dp[i]=666;
int pre=i-S;
if(pre<0)continue;
while(head<=tail&&dp[q[tail]]>=dp[pre])tail--;
q[++tail]=pre;
while(head<=tail&&i-q[head]>T)head++;
dp[i]=dp[q[head]]+mark[i];
if(i>=n)ans=min(ans,dp[i]);
}
printf("%d\n",ans);
}
}

100pts

石头数$m$很少,桥的长度$L$很大。想到压缩一些没用的点。具体如何压缩有很多方法,Luogu题解里有些很妙妙的想法:)

下面是一种$O(M\cdot T^2)$的做法。

发现可能有很长的一段都是空地,如果存在空地左边某点能到达空地右端某点,则右端的点的$dp$值可以直接由左边那个的$dp$转移过来,而不需要通过中间一大段无用的点去转移。

考虑存储真正有用的点。对于石头$i$,设其坐标为$x[i]\(,真正有用的点是\)[x[i]-T,x[i]+T]\(。同时还有这些点也要存储,\)[0]\(,\)[L-T,L+T]$。然后把这些点离散下。最多会有大概$MT$个点,接下来只要在这些点之间转移即可。

简单画一下图发现只有如下两种情况(采用填表的方式分析)。

1.从当前点$i$只跳一步能到达$j$,则更新j的dp值,这部分的复杂度为$O(T)$。

2.$i$是一块空地的左端点,尝试跳到空地的右端$j$,遇到石头则停止。

对于第二部分,如何判断能否从左端到右端?设左端为i,右端为j,只需考虑他们的距离dis。由于跳k步能到达的范围是$[ks,kt]$,所以只要找一个k,使得这个区间能够包含dis。

inline bool Jump(int dis){
int step=(dis-1)/t+1;
return s*step<=dis;
}

转移是$O(T)$的,所以整个算法时间复杂度为$O(M\cdot T^2)$。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=30500;
int sz,a[N],dp[N],ans=666,x[305];
int n,s,t,m;
bool stone[N];
inline bool Jump(int dis){
int step=(dis-1)/t+1;
return s*step<=dis;
}
int main(){
scanf("%d%d%d%d",&n,&s,&t,&m);
for(register int i=1;i<=m;++i){
scanf("%d",&x[i]);
for(int j=max(0,x[i]-t);j<=x[i]+t;j++)a[++sz]=j;
}
for(register int i=max(0,n-t);i<=n+t;++i)a[++sz]=i;
a[++sz]=0;
sort(a+1,a+sz+1);
sz=unique(a+1,a+sz+1)-a-1; for(register int i=1;i<=m;++i)stone[lower_bound(a+1,a+sz+1,x[i])-a]=1; for(register int i=1;i<=sz;++i)dp[i]=666;
dp[1]=stone[1]; for(register int i=1;i<=sz;++i){
if(dp[i]==666)continue;
dp[i]+=stone[i]; int j=i+1,boom=0;
while(j<=sz&&a[j]-a[i]<=t){
if(a[j]-a[i]>=s)dp[j]=min(dp[j],dp[i]);
boom|=stone[j++];
} while(j<=sz){
if(boom|=stone[j])break;
if(Jump(a[j]-a[i]))dp[j]=min(dp[j],dp[i]);
j++;
} if(a[i]>=n)ans=min(ans,dp[i]);
}
printf("%d",ans);
}

[NOIP2005] 过河【Dp,思维题,缩点】的更多相关文章

  1. 7月15日考试 题解(链表+状压DP+思维题)

    前言:蒟蒻太弱了,全打的暴力QAQ. --------------------- T1 小Z的求和 题目大意:求$\sum\limits_{i=1}^n \sum\limits_{j=i}^n kth ...

  2. NOIP2005过河[DP 状态压缩]

    题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数 ...

  3. codeforces 1140D(区间dp/思维题)

    D. Minimum Triangulation time limit per test 2 seconds memory limit per test 256 megabytes input sta ...

  4. Vijos 1002 过河 dp + 思维

    https://www.vijos.org/p/1002 设dp[i]表示跳到了第i个点,需要的最小的步数. 所以复杂度O(L * T), 不行 注意到T最大是10, 所以dp[i]最多只由10项递推 ...

  5. 【树形dp 思维题】HHHOJ#483. NOIP司马懿

    要注意利用一些题目的特殊条件吧. 题目大意 有一颗$n$个点带点权$a_i$的树,$q$次询问树上是否存在长度为$l$的路径. $n,q,l\le 10^5,0 \le a_i \le 2$ 题目分析 ...

  6. BZOJ 2734 洛谷 3226 [HNOI2012]集合选数【状压DP】【思维题】

    [题解] 思维题,看了别人的博客才会写. 写出这样的矩阵: 1,3,9,... 2,6,18,... 4,12.36,... 8,24,72,... 我们要做的就是从矩阵中选出一些数字,但是不能选相邻 ...

  7. [Hdu-5155] Harry And Magic Box[思维题+容斥,计数Dp]

    Online Judge:Hdu5155 Label:思维题+容斥,计数Dp 题面: 题目描述 给定一个大小为\(N*M\)的神奇盒子,里面每行每列都至少有一个钻石,问可行的排列方案数.由于答案较大, ...

  8. [SHOI2007] 书柜的尺寸 思维题+Dp+空间优化

    Online Judge:Luogu-P2160 Label:思维题,Dp,空间优化 题面: 题目描述 给\(N\)本书,每本书有高度\(Hi\),厚度\(Ti\).要摆在一个三层的书架上. 书架的宽 ...

  9. [UVA12235] Help Bubu 思维题+状态定义+Dp

    Online Judge:UVA12235 Label:思维题,状态定义,状压Dp 题面: 题目描述 有一个书架,上面放了n本书,从左往右的第i本书的高度为h[i].定义书架的混乱度为连续等高段的个数 ...

随机推荐

  1. Apache Flink 整体介绍

    前言 Flink 是一种流式计算框架,为什么我会接触到 Flink 呢?因为我目前在负责的是监控平台的告警部分,负责采集到的监控数据会直接往 kafka 里塞,然后告警这边需要从 kafka topi ...

  2. Junit用断言对控制台输出进行测试

    核心思路: 在测试前,将标准输出定向到ByteArrayOutputStream中去 用输出流文件断言内容 测试完成,将标准输出修改为console 具体操作示例 基本通用复制粘贴操作 public ...

  3. 常用的一些js事件及案例

    比如金额需要显示的时候转换成有千分位,小数点后保留2位等.去编辑的时候,又要格式化,把逗号都去掉.网上找了段代码,但是再次编辑会有问题,修改了一下,代码如下: function outputMoney ...

  4. Vuex篇

    [Vuex篇] vuex源码你学会了吗? 我来免费教你!~ 1.vuex是什么?  Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的 ...

  5. 前端跨域实现的几种方式?及使用Nginx反向代理配置。

    早期为了防止CSRF(跨域请求伪造)的攻击,浏览器引入了同源策略(SOP)来提高安全性.而所谓"同源策略",即同域名(domain或ip).同端口.同协议的才能互相获取资源,而不能 ...

  6. 【HDOJ】P5056 Boring count

    题目意思是给你一个字符串和K,让你求其中有多少个字串中每个字母的出现次数不超过K次,可以等于 题目意思是很简单的,写起来也很简单,不过就是注意最后要是long long要不WA了,555~ #incl ...

  7. 关于apache 重定向设定

    本人在研究关于apache重定向的资料,在网上找了很多,但是就本人来说,方便理解的,找到了这么一个,记录了下来,原帖地址:http://www.exehack.net/8.html 关于apache配 ...

  8. 7.Struts2框架封装数据

    Struts2框架提供了很强大的数据封装的功能,不再需要使用Servlet的API完成手动封装了!! 第一种方式:属性驱动 > 提供对应属性的set方法进行数据的封装.--经常使用 * 表单的哪 ...

  9. vue 学习 cli3常用配置

    ---恢复内容开始--- cli3以后,构建的项目更加的简洁,配置文件也没有向cli2那样暴漏出来,但这并不代表cli3是不可配置的,我们只需要在根目录下添加一个vue.config.js作为项目的配 ...

  10. CSIC_716_20191207【并发编程---进程与线程】

    僵尸进程与孤儿进程 ........... 守护进程 from Multiprocessing  import Process 在 suboprocess.start( ) 的上一行,增加 subpr ...