(题面来自Luogu)

题目描述

FJ丢失了他的一头牛,他决定追回他的牛。已知FJ和牛在一条直线上,初始位置分别为x和y,假定牛在原地不动。FJ的行走方式很特别:他每一次可以前进一步、后退一步或者直接走到2*x的位置。计算他至少需要几步追上他的牛。

输入格式

第一行为一个整数t(≤10),表示数据组数;接下来每行包含一个两个正整数x和y(0<x,y≤10^5),分别表示FJ和牛的坐标。

输出格式

对于每组数据,输出最少步数。

  这个数据范围下bfs能过实在是很玄……(你以为你的dp能胜过我的bfs吗,jojo!)

  由于这题第一次扫到某点得到的就是最优解,不需要重复遍历,bfs的复杂度是O(n)的。

bfs代码:

  1. #include <iostream>
  2. #include <cstring>
  3. #include <queue>
  4. using namespace std;
  5. queue<int> q;
  6. bool vis[100010];
  7. int dis[100010];
  8. int main() {
  9. ios::sync_with_stdio(0);
  10. int t, x, y;
  11. cin >> t;
  12. while (t--) {
  13. cin >> x >> y;
  14. if (x > y) {
  15. cout << x - y << endl;
  16. continue;
  17. }
  18. while (q.size()) q.pop();
  19. memset(vis, 0, sizeof(vis));
  20. q.push(x);
  21. dis[x] = 0;
  22. vis[x] = true;
  23. while (!q.empty()) {
  24. int k = q.front();
  25. q.pop();
  26. if (k == y) {
  27. cout << dis[k] << endl;
  28. break;
  29. }
  30. for (int i = 0; i <= 2; ++i) {
  31. int v;
  32. switch (i) {
  33. case 0:
  34. v = 2 * k;
  35. break;
  36. case 1:
  37. v = k + 1;
  38. break;
  39. case 2:
  40. v = k - 1;
  41. }
  42. if (vis[v] || !v || v > 100000) continue;
  43. dis[v] = dis[k] + 1;
  44. vis[v] = true;
  45. q.push(v);
  46. }
  47. }
  48. }
  49.  return 0;
  50. }

  主要想谈一谈dp的做法。这题用区间dp的思路并不显然,写出来则非常优美。设f[i]表示到达点i的最短步数,边界状态f[x] = 0。容易想到,f[i]可以从如下状态递推而来:

  f[i] = min(f[i - 1], f[i + 1]) + 1(i为奇数)

  f[i] = min(f[i - 1], f[i + 1], f[i / 2]) + 1(i为偶数)

  这就是f的状态转移方程。只要设计出转移阶段(顺序)进行转移,最终f[y]就是所求答案。这也是本题dp设计的难点,合适的顺序安排要保证用来更新f[i]的若干状态在使用时都已经达到了最优。

  考虑到小于x的位置仅可能是从x倒退回来,那么我们可以先从f[x - 1]到f[1]倒着递推,有f[i] = f[i + 1] + 1。这样得到的每个f[i](i 属于 [1, x - 1])一定是最优解。同时,我们可以用每个f[i]更新一次f[2i]。接下来我们从f[x + 1]顺着递推到f[y],此时的每个点i满足f[i] = min(f[i], f[i - 1] + 1, f[i + 1] + 1)。最后一个参数比较特殊,它表示要考虑f[i + 1]之前被某个i*2更新的情况。现在每个f[i]都已经是最优解,我们同样要用f[i]来更新f[2i],为了避免特判,数组开两倍。

  写到这里发现一个没有被卡掉的漏洞:由于没有拿f[x]来更新f[2x],对于y = 2x的情况程序会给出由f[2x - 1]和f[2x + 1]更新出的错误答案,因此第二个循环应当从f[x]开始递推。

dp代码:(是不是短了很多呢)

  1. #include <iostream>
  2. #include <cstring>
  3. using namespace std;
  4. int f[200100];
  5. int main() {
  6. int t, x, y;
  7. cin >> t;
  8. while (t--) {
  9. cin >> x >> y;
  10. memset(f, 0x3f, sizeof(f));
  11. f[x] = 0;
  12. for (int i = x - 1; i; --i) {
  13. f[i] = f[i + 1] + 1;
  14. f[i << 1] = min(f[i << 1], f[i] + 1);
  15. }
  16. for (int i = x; i <= y; ++i) {
  17. f[i] = min(f[i], min(f[i + 1] + 1, f[i - 1] + 1));
  18. f[i << 1] = min(f[i << 1], f[i] + 1);
  19. }
  20. cout << f[y] << endl;
  21. }
  22. return 0;
  23. }

【P1588】丢失的牛——区间dp/bfs的更多相关文章

  1. 洛谷P1588 丢失的牛

    P1588 丢失的牛 158通过 654提交 题目提供者JOHNKRAM 标签USACO 难度普及/提高- 时空限制1s / 128MB 提交  讨论  题解 最新讨论更多讨论 答案下载下来是对的,但 ...

  2. 洛谷——P1588 丢失的牛

    P1588 丢失的牛 题目描述 FJ丢失了他的一头牛,他决定追回他的牛.已知FJ和牛在一条直线上,初始位置分别为x和y,假定牛在原地不动.FJ的行走方式很特别:他每一次可以前进一步.后退一步或者直接走 ...

  3. 洛谷 P1588 丢失的牛

    题目描述 FJ丢失了他的一头牛,他决定追回他的牛.已知FJ和牛在一条直线上,初始位置分别为x和y,假定牛在原地不动.FJ的行走方式很特别:他每一次可以前进一步.后退一步或者直接走到2*x的位置.计算他 ...

  4. [JZOJ]1293.气象牛[区间DP]

    Description 为了研究农场的气候,Betsy帮助农夫John做了N(1 <= N <= 100)次气压测量并按顺序记录了结果M_1-M_N(1 <= M_i <= 1 ...

  5. 区间DP 基本题集

    51 Nod 1021 石子归并 模板题,敲就完事了,注意一下这种状态转移方程有个四边形的优化(时间) #include <cstdio> #include <iostream> ...

  6. UVA - 1632 Alibaba (区间dp+常数优化)

    题目链接 设$dp[l][r][p]$为走完区间$[l,r]$,在端点$p$时所需的最短时间($p=0$代表在左端点,$p=1$代表在右端点) 根据题意显然有状态转移方程$\left\{\begin{ ...

  7. 算法复习——区间dp

    感觉对区间dp也不好说些什么直接照搬讲义了2333 例题: 1.引水入城(洛谷1514) 这道题先开始看不出来到底和区间dp有什么卵关系···· 首先肯定是bfs暴力判一判可以覆盖到哪些城市····无 ...

  8. 71: libreoj #10151 区间dp

    $des$ https://loj.ac/problem/10151 $sol$ 区间dp $f_{i, j}$ 表示区间 $[l, r]$ 合并的最大值 枚举中间点 $k$ $f_{i, j} =m ...

  9. 浙工大新生赛莫队处理+区间DP+KMP+分析题

    题目描述 读入一个长度为n的整数数列a1,a2,…,an,以及一个整数K. q组询问. 每组询问包含一个二元组(l, r), 其中1≤l≤r≤ n, 求所有满足以下条件的二元组(l2, r2)的数目: ...

随机推荐

  1. D. Road to Post Office 解析(思維)

    Codeforce 702 D. Road to Post Office 解析(思維) 今天我們來看看CF702D 題目連結 題目 略,請直接看原題. 前言 原本想說會不會也是要列式子解或者二分搜,沒 ...

  2. 配置域名与Https

    前言 在之前的内容里,我们已经实现了部署SpringBoot项目到云服务器,但是当时用的是直接通过ip+端口的方式访问的,在之后如果是想对接上自己开发的小程序的话,必须要https的地址才行,因此今天 ...

  3. vue父组件促发子组件中的方法

    实现在父组件中促发子组件里面的方法 子组件: <template> <div> 我是子组件 </div> </template> <script& ...

  4. Java学习的第二十九天

    1. 如果类中的某个属性不希望被序列化则需要transient关键字 序列化一组对象 2.无问题 3.明天学习打印流

  5. Linux 系统编程 学习:07-基于socket的网络编程2:基于 UDP 的通信

    Linux 系统编程 学习:07-基于socket的网络编程2:基于 UDP 的通信 背景 上一讲我们介绍了网络编程的一些概念.socket的网络编程的有关概念 这一讲我们来看UDP 通信. 知识 U ...

  6. python面向对象(类与对象)

    面向对象思想 关注公众号"轻松学编程"了解更多. 1.面向对象的设计思想 面向对象是基于万物皆对象这个哲学观点. 2.面向对象和面向过程的区别 面向过程 在生活中: 它是一种看待问 ...

  7. Linux杂谈:进程锁核+实时线程导致的读写锁死循环

    发现问题 公司项目测试的时候,发现运行一段时间后会出现cpu百分之百的情况. 想着可能是哪里出现了死循环,于是打算用gdb跟一下,结果gdb居然无法attach到进程...... 定位问题 查了查去, ...

  8. 热部署只知道devtools吗?JRebel不香吗?

    持续原创输出,点击上方蓝字关注我 目录 前言 JRebel收费怎么破? 什么是本地热部署? 什么是远程热部署? JRebel和devtools的区别 如何安装JRebel? 如何本地热部署? 如何远程 ...

  9. 转载-git的安装和使用

    首先是安装和配置,参考了以下博客的内容https://www.cnblogs.com/minRose/p/10286473.html 一.下载地址 https://git-scm.com/downlo ...

  10. 转载-Eclipse无法打开Eclipse MarketPlace的解决办法

    问题描述: Eclipse点击 help-->Eclipse MarketPlace 后无任何反应,无报错,打不开 解决方法: 重新安装一下 epp MarketPlace help--> ...