1952 金发姑娘和 N 头牛(差分 + 离散化--两种离散化方式)

1. 问题描述:

你可能听过关于金发姑娘和三只熊的经典故事。然而,鲜为人知的是,金发姑娘最终成了一个农民。在她的农场中,她的牛棚里有 N 头奶牛。不幸的是,她的奶牛对温度相当敏感。对于奶牛 i,使其感到舒适的温度为 Ai…Bi。如果金发姑娘将牛棚的恒温器的温度 T 设置为 T < Ai,奶牛就会觉得冷,并会产出 X 单位的牛奶。如果她将恒温器的温度 T 设置在 Ai ≤ T ≤ Bi,奶牛就会感到舒适,并会产出 Y 单位的牛奶。如果她将恒温器的温度 T 设置为 T > Bi,奶牛就会觉得热,并会产出 Z 单位的牛奶。正如所期望的那样,Y 的值始终大于 X 和 Z。给定 X,Y,Z 以及每头奶牛感到舒适的温度范围,请计算通过合理设定恒温器温度,金发姑娘可获得的最大产奶量。恒温器温度可设置为任何整数。

输入格式

第一行包含四个整数 N,X,Y,Z。接下来 N 行,每行包含两个整数 Ai 和 Bi。

输出格式

输出可获得的最大产奶量。

数据范围

1 ≤ N ≤ 20000,
0 ≤ X,Y,Z ≤ 1000,
0 ≤ Ai ≤ Bi ≤ 10 ^ 9

输入样例:

4 7 9 6
5 8
3 4
13 20
7 10

输出样例:

31
样例解释
金发姑娘可以将恒温器温度设置为 7 或 8,这样会让奶牛 1 和 4 感到舒适,奶牛 2 感到热,奶牛 3 感到冷。共可获得 31 单位牛奶。
来源:https://www.acwing.com/problem/content/description/1954/

2. 思路分析:

分析题目可以知道每一行给出的(Ai,Bi)将一个数轴分成三个部分,如下图所示:

区间(Ai,Bi)区间的贡献为Y,Ai左边区间贡献为X,Bi右边区间贡献为Z,相当于每一个对应的区间分别加上X,Y,Z,对于每一组区间都是这样操作所以最终需要求解出哪一个点的的权值是最大的,因为整个区间加上某一个数字所以属于差分的模型;由于区间的端点的取值比较大为10 ^ 9,而整个数轴的点对数量n = 20000比较少一点所以需要离散化一下(一般差分的题目都会加上离散话来增加难度,不可能直接声明长度为10 ^ 9的数组)。离散化一般有两种方式:① 使用哈希表来记录端点值的变化,c++可以使用map(map是有序的),python可以使用字典进行记录;② 手写离散化,相当于模拟map的过程,也即建立一一映射的关系,我们需要将所有的区间端点先存下来后面才可以进行离散化,可以使用数组l,r存储每一组的区间左右端点,使用一个vector或者list列表xs来存储所有的区间端点,然后去重排序,声明一个离散化数组b,遍历所有的区间端点使用二分查找出在xs的具体位置,将其映射到离散化数组b中,最终遍历一下xs求解出前缀和最大的点即可。当我们已知离散化数组之后那么求解出离散化数组的前缀和就是原数组的值。

3. 代码如下:

哈希表来实现,c++使用map来实现,map本身就是有序的,python可以使用字典来实现,后面需要按照键从小到大进行排序:

哈希表实现(如果使用c++使用map实现代码比较简单但是运行效率比较低,map每处理一次都是logn):

import collections


class Solution:
    def process(self):
        n, x, y, z = map(int, input().split())
        # 哈希表
        b = collections.defaultdict(int)
        INF = 10 ** 10
        for i in range(n):
            l, r = map(int, input().split())
            b[-INF] += x
            b[l] += y - x
            b[r + 1] += z - y
            b[INF] += -z
        res = s = 0
        # b属于一个列表, 列表元素为元组, 是一个键值对
        b = sorted(b.items(), key=lambda x: x[0])
        for k, v in b:
            s += v
            res = max(res, s)
        return res


if __name__ == "__main__":
    print(Solution().process())

手写离散化实现(如果使用c++代码数组实现代码比较难写但是运行效率比较高):

from typing import List


class Solution:
    # 二分寻找x在xs的位置
    def find(self, xs: List[int], x: int):
        l, r = 0, len(xs) - 1
        while l < r:
            mid = l + r >> 1
            if xs[mid] >= x:
                r = mid
            else:
                l = mid + 1
        return r

    def process(self):
        n, x, y, z = map(int, input().split())
        l, r = list(), list()
        # 因为数据最大是10 ** 9所以边界大一点就可以
        INF = 10 ** 10
        N = 20010
        # 左右边界表示正无穷与负无穷
        xs = [-INF, INF]
        # b为离散化数组
        b = [0] * (N * 2)
        for i in range(n):
            t1, t2 = map(int, input().split())
            # 先需要存储下来所有端点才可以进行离散化
            l.append(t1)
            r.append(t2)
            xs.append(t1)
            # 注意是t2 + 1, 右端点加1的位置
            xs.append(t2 + 1)
        # 去重, 这样可以确保一一映射
        xs = list(set(xs))
        # 从小到大进行排序
        xs.sort()
        for i in range(n):
            L, R = self.find(xs, l[i]), self.find(xs, r[i] + 1)
            # 与map的本质是一样的
            b[0] += x
            b[L] += y - x
            b[R] += z - y
            b[-1] += -z
        res = s = 0
        for i in range(len(xs)):
            s += b[i]
            res = max(res, s)
        return res


if __name__ == "__main__":
    print(Solution().process())
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值