(题面来自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. }

最新文章

  1. Sql Server之旅——第四站 你必须知道的非聚集索引扫描
  2. 微信小程序一步步搭建商城系列-01-开篇
  3. &lt;转&gt;修改TM2013聊天记录保存目录final版
  4. android拍照选择图片上传服务器自定义控件
  5. Linux下解决“shutdown: command not found&quot;问题
  6. [SQL] 待整理3
  7. gdb调试SAPI方式的php
  8. ACdream: Sum
  9. Nmap绕过防火墙&amp;脚本的使用
  10. mysql 开发进阶篇系列 22 磁盘I/O问题(从linux操作系统上优化)
  11. [leetcode]Recover Binary Search Tree @ Python
  12. Qt5.3.2_CentOS6.4_x86_调试源码关联【勿删,简洁】
  13. java编译需要文件后缀名.java 而运行不需要后缀名.class
  14. struts2(s2-052)远程命令执行漏洞复现
  15. 2017年浙江工业大学大学生程序设计迎新赛预赛 H - 栗酱的文明
  16. MYSQL注入天书之前言
  17. 文件打开的过程——调用fd=open()时操作系统所做的工作
  18. 在windows搭建jenkins測试环境
  19. pyhton3 一些排序算法概括
  20. 关于wpf中popup跟随鼠标移动显示

热门文章

  1. 【转】Optimized Surface Loading and Soft Stretching
  2. 如何在Debian上安装和使用PHP Composer
  3. 打印Sql查询语句
  4. css3 渐变 兼容
  5. 【SpringBoot】07.SpringBoot文件上传
  6. 面试 之 nginx,负载,动静分离
  7. 【应用服务 App Service】App Service 新手资料包
  8. 重置ubuntu13.04 密码
  9. 入坑 docsify,一款神奇的文档生成利器!
  10. JavaScript监听页面可见性(焦点)同时改变title的三种方法