zoukankan      html  css  js  c++  java
  • P3842 [TJOI2007]线段——线性dp

    P3842 [TJOI2007]线段

    题目描述

    在一个 (n imes n) 的平面上,在每一行中有一条线段,第 (i) 行的线段的左端点是 ((i, L(i))),右端点是 ((i, R(i))),其中 (1leq L(i)leq R(i)leq n)

    你从 ((1, 1)) 点出发,要求沿途走过所有的线段,最终到达 ((n, n)) 点,且所走的路程长度要尽量短。

    更具体一些说,你在任何时候只能选择向下走一步(行数增加 (1))、向左走一步(列数减少 (1))或是向右走一步(列数增加 (1))。当然,由于你不能向上行走,因此在从任何一行向下走到另一行的时候,你必须保证已经走完本行的那条线段。

    输入格式

    输入文件的第一行有一个整数 (n),以下 (n) 行,在第 (i) 行(总第 (i+1) 行)的两个整数表示 (L(i))(R(i))

    输出格式

    输出文件仅包含一个整数,你选择的最短路程的长度。

    输入输出样例

    输入

    6
    2 6
    3 4
    1 3
    1 2
    3 6
    4 5
    

    输出

    24
    

    说明/提示

    我们选择的路线是

    ((1,1)->(1,6))
    ((2,6)->(2,3))
    ((3,3)->(3,1))
    ((4,1)->(4,2))
    ((5,2)->(5,6))
    ((6,6)->(6,4)->(6,6))

    不难计算得到,路程的总长度是 (24)

    (100\%) 的数据中,(nleq 20,000)

    思路

    不难看出,路程长度由两个部分组成:左右走的距离 (+) 上下走的距离,因为上下走的距离一定是 (n),所以我们只需要求出左右走的最小距离即可。

    很明显,当我们走完一行的线段时,我们会处在左端点/右端点,这样我们就需要在线性 (dp) 的基础上加一维来表示走完这一行处在左/右端点。

    所以我们定义一个 (f[i][0/1]),表示走完第 (i) 行的线段后,我们处在左/右端点。

    然后我们就可以用上一行的状态来推出下一行了。

    分为 (4) 种情况:

    上一行在左端点,下一行走到左端点;
    上一行在左端点,下一行走到右端点;
    上一行在右端点,下一行走到左端点;
    上一行在右端点,下一行走到右端点;

    我们以"上一行在右端点,下一行走到右端点"为例:

    这样我们就可以推出状态转移方程了。

    f[i][1]=min(f[i-1][1]+abs(l[i]-r[i-1])+r[i]-l[i]+1,f[i-1][0]+abs(l[i]-l[i-1])+r[i]-l[i]+1);//本行走到右端点,上一行走到左/右端点,取最小值
    f[i][0]=min(f[i-1][1]+abs(r[i]-r[i-1])+r[i]-l[i]+1,f[i-1][0]+abs(r[i]-l[i-1])+r[i]-l[i]+1);//本行走到左端点,上一行走到左/右端点,取最小值
    

    最后不要忘了初始化第 (1) 行就可以了。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn=2e4+50;
    int n;
    int l[maxn],r[maxn];
    int f[maxn][2];
    
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&l[i],&r[i]);
        }
        f[1][0]=2*r[1]-l[1]-1;//第1行往返,走到右端点再回来
        f[1][1]=r[1]-1;//直接走到右端点
        for(int i=2;i<=n;i++){
            f[i][1]=min(f[i-1][1]+abs(l[i]-r[i-1])+r[i]-l[i]+1,f[i-1][0]+abs(l[i]-l[i-1])+r[i]-l[i]+1);//本行走到右端点,上一行走到左/右端点,取最小值
            f[i][0]=min(f[i-1][1]+abs(r[i]-r[i-1])+r[i]-l[i]+1,f[i-1][0]+abs(r[i]-l[i-1])+r[i]-l[i]+1);//本行走到左端点,上一行走到左/右端点,取最小值
        }
        printf("%d
    ",min(f[n][1]+n-r[n],f[n][0]+n-l[n]));//因为最后还要走到(n,n)的地方,加上剩下的距离,再加上上下走的距离即可
        return 0;
    }
    
  • 相关阅读:
    [Django]中间件
    Python装饰器的诞生过程-->和闭包的微妙关系
    Python实现经典算法之---斐波那契数列(兔子问题&走楼梯问题)
    二分查找---非递归算法和递归算法
    JavaScript的event对象
    input[type='submit']input[type='button']button等按钮在低版本的IE下面,去掉黑色边框的问题
    JavaScript 之 数据在内存中的存储和引用
    HTML5 之 简单汇总
    HTML5 Canvas——基础入门
    nodejs(14)express获取url中的参数
  • 原文地址:https://www.cnblogs.com/Rubyonly233/p/13287304.html
Copyright © 2011-2022 走看看