1. 问题描述:
农夫约翰发明了一种绝妙的方法来粉刷牛棚旁边的长栅栏(把栅栏想象成一维的数轴)。他只需要在他最喜欢的奶牛贝茜身上挂一个刷子,然后在一旁悠闲的喝凉水就行了。贝茜沿着栅栏来回走动时,会将她走过的栅栏部分涂上油漆。贝茜从栅栏上的位置 0 处开始,共进行 N 次移动。移动可能形如 10 L,表示向左移动 10 单位距离,也可能形如 15 R,表示向右移动 15 单位距离。给定贝茜的 N 次移动列表,约翰想知道至少被涂抹了 2 层油漆的区域的总长度。整个行进过程中,贝茜距离出发地的距离不会超过 10 ^ 9。
输入格式
第一行包含一个整数 N。接下来 N 行,每一行包含一个行动指令,诸如 10 L 或 15 R。
输出格式
输出至少被涂抹了 2 层油漆的区域的总长度。
数据范围
1 ≤ N ≤ 10 ^ 5
整个行进过程中,贝茜距离出发地的距离不会超过 10 ^ 9。
每次指令移动距离的取值范围是 [1,2 × 10 ^ 9]。
输入样例:
6
2 R
6 L
1 R
8 L
1 R
2 R
输出样例:
6
样例解释
共有 6 单位长度的区域至少被涂抹 2 层油漆。这些区域为 (−11,−8),(−4,−3),(0,2)。
来源:https://www.acwing.com/problem/content/1989/
2. 思路分析:
分析题目可以知道实际上是将某一个区间加上1,所以属于经典的差分算法,但是这道题目离原点的距离最大是10 ^ 9,而且最多操作次数是10 ^ 5所以我们需要先离散化一下,这里为了方便操作可以使用哈希表进行映射,这道题目求解的实际上是单位距离所以可以理解为左闭右开的区间,也即对于区间[l,r],map[r + 1 - 1] ++ ,map[l]++,当我们离散化之后实际上需要求解的是次数大于等于2的区间中的点的数量,差分的前缀和就是对应的区间中点出现的次数(原数组就是差分数组的前缀和),而且每两个点之间出现的次数是相等的,为两个点位置的差,所以不用累加每一个位置的和,直接计算一整段的前缀和即可。(这道题目其实也可以看成是二维扫描线的一维简化版本,更容易理解,可以看成是左闭右开的区间,只需要操作区间端点的值即可)
3. 代码如下:
from typing import List
class Solution:
def process(self):
n = int(input())
x = 0
# 使用字典来模拟哈希表
b = dict()
for i in range(n):
t = input().split()
y, s = int(t[0]), t[1]
# 差分
if s == "R":
# 先判断在字典中是否存在当前的键, 不存在那么将其置为-1, 这样才不会发生字典的键值对异常
if x + y not in b:
b[x + y] = -1
else:
b[x + y] -= 1
if x not in b:
b[x] = 1
else:
b[x] += 1
x += y
else:
if x - y not in b:
b[x - y] = 1
else:
b[x - y] += 1
if x not in b:
b[x] = -1
else:
b[x] -= 1
x -= y
# 根据字典的键从小到大排序
b = sorted(b.items(), key=lambda x: x[0])
_sum = res = last = 0
for k, v in b:
# 出现次数大于等于2说明满足要求
if _sum >= 2: res += k - last
last = k
_sum += v
print(res)
if __name__ == "__main__":
Solution().process()