4072 习题册(堆-使用标记数组实现延迟删除)

1. 问题描述:

某书店出售 n 套习题册。每套习题册可以用三个整数 pi,ai,bi 来描述,表示该习题册的价格为 pi,前半部分内容考察知识点 ai,后半部分内容考察知识点 bi。已知,所有 pi 各不相同,ai 和 bi 都是 1∼3 的整数,且 ai 和 bi 可能相等。有 m 个学生前来购买习题册。这 m 个学生是一个接着一个来的,前一个学生走后,后一个学生才会到店。每个学生都只想买 1 套习题册。第 i 个学生希望重点练习知识点 ci,所以在轮到第 i 个学生进行选购时,他只会挑选包含知识点 ci 的习题册进行购买,如果这样的习题册不止一本,他就会挑选最便宜的那个购买,如果这样的习题册一本都没有,他就会放弃购买,直接走人。请你计算,每个学生购买习题册花了多少钱。

输入格式

第一行包含整数 n。第二行包含 n 个整数 p1,p2,…,pn。第三行包含 n 个整数 a1,a2,…,an。第四行包含 n 个整数 b1,b2,…,bn。第五行包含整数 m。第六行包含 m 个整数 c1,c2,…,cm。

输出格式

共一行,输出 m 个整数,第 i 个整数表示第 i 个学生购买习题册的花费,如果该学生什么也没买,则输出 −1。

数据范围

前三个测试点满足,1 ≤ n,m ≤ 10。
所有测试点满足,1 ≤ n,m ≤ 2 × 10 ^ 5,1 ≤ ai,bi,ci ≤ 3,1 ≤ pi ≤ 10 ^ 9。

输入样例1:

5
300 200 400 500 911
1 2 1 2 3
2 1 3 2 1
6
2 3 1 2 1 1

输出样例1:

200 400 300 500 911 -1

输入样例2:

2
1000000000 1
1 1
1 2
2
2 1

输出样例2:

1 1000000000
来源:https://www.acwing.com/problem/content/4075/

2. 思路分析:

分析题目可以知道数据范围为10 ^ 5,所以我们需要将时间复杂度控制在O(nlogn)以内,时间复杂度为O(nlogn)之内的算法有线段树,平衡树,树状数组,set/map,堆,我们需要考虑一下使用哪一种算法来解决。由题目可知,我们需要从所有包含ci的练习册中选择一本最便宜的练习册,我们可以对练习册按照知识点进行分类,因为知识点都是1~3的整数,所以可以将练习册按照知识点分为三大类,对应的编号为0,1,2,我们需要选择一种数据结构支持两种操作:① 取价格的最小值;② 删除最小值;堆这种数据结构就支持这两种操作,对于c++语言来说可以使用priority_queue来实现一个堆,由于每一本书可能属于多个类,所以当我们在一个类中选择了这本书之后那么需要将这本书在另外一个类中删掉,但是c++中的priority_queue不支持删除任意元素的操作,堆本身是支持删除任意元素的操作的,如果使用c++中的set或者mulset是可以支持直接删除的,其实这里有一个非常常用的套路,我们可以声明一个布尔数组用来标记哪些是已经删除掉的元素,如果发现堆顶元素标记过了说明这个元素不能够再使用了直接从从堆中弹出,直到堆为空或者找到一个堆顶没有标记的元素为止,这样实际上是实现了延迟删除的操作,可以模拟删除堆中任意元素的操作;对于python语言则可以声明一个列表h,每一个h[i]实际上是一个列表,使用heapq模块对列表的h[i]进行操作就可以实现堆的相关操作。

3. 代码如下:

import heapq


class Solution:
    def process(self):
        n = int(input())
        # h对应堆列表, 使用heapq模块进行操作相当于实现的是堆的相关操作
        # 每一个h[i]存储对应知识点的书籍编号
        h = [list() for i in range(3)]
        # p为价格列表
        p = list(map(int, input().split()))
        # a为前半部分的知识点
        a = list(map(int, input().split()))
        # b为后半部分的知识点
        b = list(map(int, input().split()))
        m = int(input())
        # c为希望买到的书的知识点
        c = list(map(int, input().split()))
        for i in range(n):
            # 小根堆
            heapq.heappush(h[a[i] - 1], (p[i], i))
            heapq.heappush(h[b[i] - 1], (p[i], i))
        # st为标记列表, 用来标记书的是否已经卖掉了
        st = [0] * (n + 10)
        for i in range(m):
            # x当前的知识点编号
            x = c[i] - 1
            # 弹出之前已经卖掉的练习册, h[x][0]表示堆顶元素, h[x][0][1]表示堆顶元素编号
            while h[x] and st[h[x][0][1]] == 1:
                heapq.heappop(h[x])
            # 如果当前有元素说明可以满足当前的要求
            if len(h[x]):
                # 删除掉当前的书籍
                t = heapq.heappop(h[x])
                st[t[1]] = 1
                print(t[0], end=" ")
            else:
                # 说明当前需要的知识点的书籍已经卖完了
                print(-1, end=" ")


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值