1913 公平摄影(枚举 + 前缀和 + 哈希表)

1. 问题描述:

农夫约翰的 N 头奶牛站在一维长围栏的不同位置。第 i 头牛位于位置 xi,其所属品种为 bi(根西岛牛或荷斯坦牛)。所有奶牛的位置各不相同。约翰想给一段连续区间内的奶牛拍摄一张照片,用来在乡村集市上展览。但是我们希望他所有品种的奶牛都能在照片中得到公平的展示。因此,他希望确保无论照片中出现哪些品种的奶牛,每种品种的奶牛在照片中的数量都必须相等。例如,一张照片中只包含荷斯坦牛是可以的,包含荷斯坦牛和根西岛牛各 27 头也没问题,但是包含 10 头荷斯坦牛和 9 头根西岛牛则不可以。请确定,约翰可以拍下的满足以上条件的照片的最大尺寸。照片的尺寸是指照片中奶牛最大和最小位置之间的差。约翰最终可能只拍下一头奶牛,这种情况下,照片尺寸为 0。

输入格式

第一行包含整数 N。接下来 N 行,每行包含一个整数 xi 和一个字符 bi(H 表示荷斯坦牛,G 表示根西岛牛)。

输出格式

输出照片的最大尺寸。

数据范围

1 ≤ N ≤ 10 ^ 5,
0 ≤ xi ≤ 10 ^ 9

输入样例:

6
4 G
10 H
7 G
16 G
1 G
3 H

输出样例:
7
样例解释
共 6 头牛,从左到右排列顺序为 G,H,G,G,H,G。最佳摄影方案是拍中间四头奶牛,恰好荷斯坦牛和根西岛牛各两头。
来源:https://www.acwing.com/problem/content/1915/

2. 思路分析:

分析题目可以知道只存在两种品种的牛,所以可以将牛看成是0和1,由题目可知我们需要找到最大的区间使得每种牛的数量相等,区间中每种牛的数量相等对应三种情况:分别是区间中全0,全1或者0和1的数目相等,全0和全1的情况比较好判断我们只需要计算连续相等一段的区间长度即可,声明一个last变量枚举的时候更新区间长度即可,所以主要是存在0和1的情况的判断,因为只存在两种品种的牛所以有一个常用的技巧是令一种牛的权重为1,另外一种牛的权重为-1这样当区间和为0的时候说明当前区间0和1的数目相等,如何在枚举的时候计算区间和是否为0呢,这里也有一个常用的技巧:使用一个变量维护前缀和sum,并且结合哈希表维护的前缀和信息查询第一次出现当前sum的位置,如果存在说明存在某一个位置到当前位置的前缀和为0所以在枚举的时候维护前缀和和哈希表即可,这里需要注意的是最终求解的答案是两个区间的位置之差,所以在枚举的时候维护前缀和和哈希表的时候需要注意代码编写的位置。

3. 代码如下:

class Solution:
    def process(self):
        n = int(input())
        q = list()
        for i in range(n):
            s = input().split()
            # 当品种为G的时候令其为1否则为-1这样在计算有0和1的时候直接计算前缀和为0的区间即可
            if s[1] == "G":
                q.append((int(s[0]), 1))
            else:
                q.append((int(s[0]), -1))
        # 从小到大排序
        q.sort(key=lambda x: x[0])
        # last为连续相等的一段第一次出现的位置
        last = res = _sum = 0
        dic = dict()
        for i in range(n):
            # 先判断是否在哈希表中先判断表示不包含第i个位置的前缀和
            if _sum not in dic:
                dic[_sum] = q[i][0]
            # 计算前缀和
            _sum += q[i][1]
            if _sum in dic:
                # 更新0和1相等的最大位置之差
                res = max(res, q[i][0] - dic[_sum])
            # 不相等的时候说明新的一段就开始了
            if i == 0 or q[i][1] != q[i - 1][1]:
                last = q[i][0]
            res = max(res, q[i][0] - last)
        return res


if __name__ == "__main__":
    print(Solution().process())
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值