1987 粉刷栅栏(差分)

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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值