• 【P1588】丢失的牛——区间dp/bfs


    (题面来自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. }  
  • 相关阅读:
    hdu1316
    MVC中的ViewData、ViewBag和TempData
    linux下性能监控工具
    【翻译自mos文章】执行utlpwdmg.sql之后报ORA-28003, ORA-20001, ORA-20002, ORA-20003, ORA-20004 错误
    HTTP协议的消息头:Content-Type和Accept的作用 转载https://www.cnblogs.com/lexiaofei/p/7289436.html
    HTTP协议的消息头:Content-Type和Accept的作用
    JWT(JSON Web Token) 多网站的单点登录,放弃session 转载https://www.cnblogs.com/lexiaofei/p/7409846.html
    Http协议中get和post的区别
    常用的HTTP请求头与响应头
    浏览器获取自定义响应头response-headers
  • 原文地址:https://www.cnblogs.com/TY02/p/11321962.html
Copyright © 2020-2023  润新知