#lydlx05x0E18dc. 围栏障碍训练场 Fence Obstacle Course
围栏障碍训练场 Fence Obstacle Course
题目描述
农夫约翰为他的奶牛们建造了一个围栏障碍训练场,以供奶牛们玩耍。
训练场由 个不同长度的围栏组成,每个围栏都与 轴平行,并且第 个围栏的 坐标为 。

训练场的出口位于原点,起点位于
这些牛会从起点处开始向下走,当它们碰到围栏时会选择沿着围栏向左或向右走,走到围栏端点时继续往下走,按照此种走法一直走到出口为止。
请问,这些牛从开始到结束,行走的水平距离最少为多少。
输入格式
第一行包含两个整数 和 。
第 行,每行包含两个整数 ,表示一个围栏的起始横坐标和结束横坐标,其中第 行表示第 个围栏的数据。
起点坐标满足 。
输出格式
输出一个整数,表示最小水平行走距离。
样例
4 0
-2 1
-1 2
-3 0
-2 1
4
样例解释
输入
围栏(从下往上编号 1 到 4): 1: 2: 3: 4:
起点在 ,即第 4 层横坐标 0 处。
行走过程
牛从 垂直向下走,碰到第 4 层围栏(,横坐标范围 ),因为起点横坐标 0 在围栏内,所以牛必须选择向左或向右走到围栏端点,然后垂直向下。
我们需要计算所有可能路径中的最小水平距离。
计算
可以采用动态规划从下往上计算。
定义 表示到达第 层围栏左端点 时的最小总水平距离, 表示到达第 层围栏右端点 时的最小总水平距离。
从第 层到第 层的转移:
- 如果从第 层左端点 垂直向下,会落到第 层横坐标 处,如果 在第 层围栏内部(),则牛必须走到第 层的左端点或右端点。否则,如果 在围栏左边(),则直接落在第 层左端点;如果 在围栏右边(),则直接落在第 层右端点。
- 类似地处理从第 层右端点 垂直向下的情况。
具体转移公式较复杂,需要分类讨论。
另一种思路:从下往上考虑,用两个变量 和 表示当前层(第 层)围栏的左端点和右端点对应的最小水平距离。
但更常见的方法是从上往下动态规划,但由于牛是从上往下走,我们需要知道从起点到每个围栏端点的最小水平距离。
样例计算
我们手动计算样例。
围栏从上到下( 到 ):
-
第 4 层:,起点 在内部,所以牛必须向左走到 -2 或向右走到 1。 从起点到左端点水平距离 = 到右端点水平距离 = 所以
-
第 3 层: 从第 4 层左端点 垂直向下落到 ,检查是否在第 3 层围栏内: 包含 -2,所以需要走到左端点或右端点。
- 走到左端点 :水平距离增加 ,总距离 。
- 走到右端点 :水平距离增加 ,总距离 。 从第 4 层右端点 垂直向下落到 ,,超出围栏右端点,所以直接落到第 3 层右端点 ,水平距离增加 ,总距离 (注意:从 落到 是因为围栏右端点是 0,牛必须走到端点才能向下?实际上,如果落点超出围栏,牛会直接落到最近的端点,然后垂直向下?题目描述:“当它们碰到围栏时会选择沿着围栏向左或向右走,走到围栏端点时继续往下走”。所以如果垂直下落时没有碰到围栏(即落点不在围栏上),则直接继续下落;如果落点在围栏上,则必须沿围栏走到端点再下落。
因此,对于第 4 层右端点 垂直下落,落点 不在第 3 层围栏 内,所以直接继续下落(即落到第 2 层?不对,下落过程会依次经过每一层。当从第 4 层端点下落时,首先到达第 3 层高度 ,如果 不在第 3 层围栏上,则继续下落至第 2 层,直到碰到某个围栏或地面。所以我们需要考虑从当前端点下落,会落到下面的哪个围栏。
因此,我们需要知道从某个点垂直下落,会碰到下面哪一层的围栏(如果碰到)。这可以通过预处理每个围栏的覆盖区间,然后对于每个端点,找到下面第一个包含该 坐标的围栏。
由于 较大(30000),需要高效算法。
更高效的算法:线段树优化 DP
一种经典做法是使用动态规划结合线段树或平衡树优化。
定义 和 如上。
转移时,对于第 层的左端点 ,我们需要知道从 垂直下落会落到哪里。设下落过程中碰到的下一个围栏是 (),且 在围栏 的内部(),那么牛必须从 走到 或 。因此:
$$dp[i][0] = \min( dp[j][0] + |A_i - A_j|, dp[j][1] + |A_i - B_j| )$$其中 是下面第一个满足 的围栏。如果不存在这样的 ,则牛会直接落到地面(原点),此时水平距离为 。
类似地,对于右端点 :
$$dp[i][1] = \min( dp[j][0] + |B_i - A_j|, dp[j][1] + |B_i - B_j| )$$因此,我们需要快速找到每个端点垂直下落碰到的下一个围栏。这可以通过从下往上扫描,用线段树维护当前每个坐标点被哪个围栏覆盖。
具体步骤:
- 将围栏按 坐标(即层数 )从小到大排序(输入已经按层给出,第 行就是第 层)。
- 从下往上处理( 从 到 ),用线段树维护区间 被第 层覆盖。对于端点 和 ,查询 和 处当前被哪个围栏覆盖(即下面最近的围栏)。由于我们从下往上处理,查询时得到的是已经处理过的下面某层围栏 ()。
- 然后计算 和 。
- 最后,对于起点 ,我们需要找到从 下落碰到的第一个围栏(应该是第 层本身,因为起点在第 层上?实际上起点在第 层围栏上,所以牛一开始就在围栏上,必须走到左端点或右端点。因此,最小水平距离为 。
样例验证
我们按照此方法计算样例。
围栏(层数从 1 到 4): 1: 2: 3: 4:
从下往上处理:
第 1 层: 下面没有围栏(地面),所以 ,。
第 2 层: 查询 :当前线段树中 被第 1 层覆盖(因为第 1 层 包含 -1),所以 。 $dp[2][0] = \min( dp[1][0] + | -1 - (-2) |, dp[1][1] + | -1 - 1 | ) = \min(2+1, 1+2) = \min(3,3) = 3$ 查询 : 不在第 1 层围栏内(第 1 层 ),所以下面没有围栏,直接到地面:
第 3 层: 查询 :不在第 2 层和第 1 层内,所以直接到地面: 查询 :在第 1 层内( 包含 0),: $dp[3][1] = \min( dp[1][0] + |0 - (-2)|, dp[1][1] + |0 - 1| ) = \min(2+2, 1+1) = \min(4,2) = 2$
第 4 层: 查询 :在第 3 层内?第 3 层 包含 -2,所以 : $dp[4][0] = \min( dp[3][0] + | -2 - (-3) |, dp[3][1] + | -2 - 0 | ) = \min(3+1, 2+2) = \min(4,4) = 4$ 查询 :在第 2 层内?第 2 层 包含 1,所以 : $dp[4][1] = \min( dp[2][0] + |1 - (-1)|, dp[2][1] + |1 - 2| ) = \min(3+2, 2+1) = \min(5,3) = 3$
起点 在第 4 层上: 最小水平距离 = $\min( dp[4][0] + |0 - (-2)|, dp[4][1] + |0 - 1| ) = \min(4+2, 3+1) = \min(6,4) = 4$
输出 4,与样例一致。
数据范围
算法总结
- 读入 和围栏数据。
- 将坐标离散化(因为 范围较大但 较小,最多 个端点)。
- 从下往上( 从 到 )处理围栏:
- 对于端点 和 ,在线段树上查询该 坐标当前被哪个围栏覆盖(即下面最近的围栏 )。
- 计算 和 。
- 将区间 在线段树上标记为被第 层覆盖(覆盖信息可以记录层号 )。
- 最后计算起点 的最小水平距离:找到 垂直下落碰到的第一个围栏(即第 层),然后按公式计算。
- 输出结果。
线段树设计
线段树需要支持:
- 区间赋值:将区间 覆盖为层号 。
- 单点查询:查询某个 坐标当前被哪个层覆盖。
由于是区间赋值,可以用懒标记线段树。
复杂度
- 离散化
- 线段树操作
- 总复杂度 ,可以处理 。
注意事项
- 起点 可能不在任何围栏上?题目保证 ,所以起点在第 层围栏上。
- 地面视为原点 ,如果垂直下落没有碰到任何围栏,则水平距离就是 。
- 注意坐标可能为负,离散化时需注意。