洛谷 P3842. P3842 [TJOI2007] 线段
[TJOI2007] 线段
题目描述
在一个 \(n \times n\) 的平面上,在每一行中有一条线段,第 \(i\) 行的线段的左端点是\((i, L_{i})\),右端点是\((i, R_{i})\)。
你从 \((1,1)\) 点出发,要求沿途走过所有的线段,最终到达 \((n,n)\) 点,且所走的路程长度要尽量短。
更具体一些说,你在任何时候只能选择向下走一步(行数增加 \(1\))、向左走一步(列数减少 \(1\))或是向右走一步(列数增加 \(1\))。当然,由于你不能向上行走,因此在从任何一行向下走到另一行的时候,你必须保证已经走完本行的那条线段。
输入格式
第一行有一个整数 \(n\)。
以下 \(n\) 行,在第 \(i\) 行(总第 \((i+1)\) 行)的两个整数表示 \(L_i\) 和 \(R_i\)。
输出格式
仅包含一个整数,你选择的最短路程的长度。
样例 #1
样例输入 #1
6
2 6
3 4
1 3
1 2
3 6
4 5
样例输出 #1
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\%\) 的数据中,\(n \le 2 \times 10^4\),\(1 \le L_i \le R_i \le n\)。
题解
不属于某个模板 利用dp的思想来分析
一篇很详细的题解 要把每种复杂的情况都考虑到了 各种情况最后状态方程是相同的
https://www.luogu.com.cn/article/w2ixdvdc
关键点
关键点就是一定要确保将当前行的线段走完,比如从上一行的左端点下来就一定要先走到当前行线段的右端点再走到左端点 确保当前线段已经被走完
简单说就是 一定要有dis(r[i], l[i])
一个例子

走到当前行左端点的情况
分为两种 从上一行左端点下来 或者 从上一行右端点下来 取最短路径 记得要加上从上面一行下来的距离1
f[i][0] = min(f[i - 1][0] + dis(l[i - 1], r[i]) + dis(r[i], l[i]), f[i - 1][1] + dis(r[i - 1], r[i]) + dis(r[i], l[i])) + 1;
走到当前行右端点的情况
f[i][1] = min(f[i - 1][0] + dis(l[i - 1], l[i]) + dis(l[i], r[i]), f[i - 1][1] + dis(r[i - 1], l[i]) + dis(l[i], r[i])) + 1;
#include <bits/stdc++.h>
using namespace std;
const int N = 2e4 + 10;
//观察数据范围 答案不会超过10^9 不用开成long long
int f[N][2]; //两个状态 最终走到该行的左边界 和 最终走到该行的右边界
int l[N], r[N]; //第i行的左右边界对应坐标
int n;
//计算当前行两点之间的距离
int dis(int x, int y)
{
return abs(x - y); //abs()是两数相减取其绝对值的函数
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d%d", &l[i], &r[i]);
//由于第一行没有第i-1行 我们单独处理
f[1][0] = dis(r[1], 1) + dis(r[1], l[1]); //计算从(1, 1)走到左端点的距离, 要先走到右端点 再到左端点 才能走完这段线段
f[1][1] = dis(r[1], 1); //走到右端点的距离
for (int i = 2; i <= n; i ++ )
{ //关键点就是一定要将当前行的线段走完,比如从上一行的左端点下来就一定要先走到当前行线段的右端点再走到左端点 确保当前线段已经被走完
f[i][0] = min(f[i - 1][0] + dis(l[i - 1], r[i]) + dis(r[i], l[i]), f[i - 1][1] + dis(r[i - 1], r[i]) + dis(r[i], l[i])) + 1; //走到当前行左端点的情况分为两种 从上一行左端点下来 或者 从上一行右端点下来 取最短路径 记得要加上 下来的距离1
f[i][1] = min(f[i - 1][0] + dis(l[i - 1], l[i]) + dis(l[i], r[i]), f[i - 1][1] + dis(r[i - 1], l[i]) + dis(l[i], r[i])) + 1; //走到当前行右端点的情况
}
int res = min(f[n][0] + dis(n, l[n]), f[n][1] + dis(r[n], n)); //取出走到第n行的最小值 记得最后还要走到(n, n) 还要分别加上最终走到左右端点时到(n, n)的距离
printf("%d\n", res);
return 0;
}

浙公网安备 33010602011771号