Python 2020华为提前批机试--任务调度求最大收益

输入:
第一行输入总共的任务数量m
接下来m行,每行两个数字,表示任务的截止时间和该任务的收益(1单位时间可以完成一个任务)
输出:
最大的收益

这类题往往采用贪心+优先队列的思想进行求解,代码如下:

from heapq import *
m = int(input())
arr = []
for _ in range(m):
    arr.append(list(map(int, input().split())))
def funer(arr):
    arr.sort()
    pq = [arr[0][1]]  
    heapify(pq)
    for i in range(1, len(arr)):
        heappush(pq, arr[i][1])  # 按截止时间先后顺序,存储任务的收益值
        # 如果当前时刻任务的截止时间和上一时刻任务的截止时间相同:
        # 如果此时优先队列的任务数量大于当前任务的截止时间
        # 则需要将队列里最小收益弹出(表示不选择该任务)
        # 不好理解的话,这里取[1,19],[1,25]为例,当i==2,此时pq = [19, 25],这两个任务的截止时间都是1,len(pq) > 1,所以需要将收益值19这个任务抛弃。
        if arr[i][0] == arr[i - 1][0]: 
            if len(pq) > arr[i][0]:
                heappop(pq)
        # print(i, pq)
    res = 0
    # 最后队列里面的收益值所对应的任务即为所选的任务
    while pq:
        res += heappop(pq)
    return res
out = funer(arr)
print(out)
'''
5
2 100
1 19
2 27
1 25
3 13
out:
140
'''

其他类似的题型:

最多可以参加的会议数目

在这里插入图片描述

# 核心思想就是把每个开始时间不大于当前时间(i)的结束时间放到堆中,然后判断结束时间是不是大于当前,
# 不是直接扔掉,是就扔掉答案的同时记录一次答案。
class Solution:
    def maxEvents(self,events):
        res, T = 0, 0
        d = collections.defaultdict(list)
        for s, e in events:
            d[s].append(e)
            T = max(T, e)
        q = []
        for i in range(1, T + 1):
            for end in d[i]:
                heapq.heappush(q, end)
            del d[i] # 优化字典,将此刻时间的会议去掉
            while q:
                cur = heapq.heappop(q) #结束时间比当前开始时间早的都弹出
                if cur >= i: # 找到符合条件的,则更新res,跳出循环
                    res += 1
                    break
        return res

吃苹果的最大数目

在这里插入图片描述

# 优先吃掉最早过期的苹果
from heapq import *
class Solution:
    def eatenApples(self, apples: List[int], days: List[int]) -> int:
        q, res, i = [], 0, 0
        n = len(apples)
        while i < n or q:
            if i < n and apples[i] > 0:
                heappush(q, [i + days[i], apples[i]])
            while q and (q[0][0] <= i or q[0][1] <= 0):
                heappop(q)
            if q:
                q[0][1] -= 1
                res += 1
            i += 1
        return res

2022一点资讯某题

给定一天的时间段数组,在每个时间段内参加一个活动,求这一天最多可以参加几个活动。(开始时间一定是早于结束时间,如果前一个活动在某一时刻结束,可以再去参加同一时刻开始的另一个活动)
输入:
times = [[“10:00”,“12:00”],[“03:00”,“11:30”],[“11:30”,“14:00”]]
输出:
2
(核心代码模式)

这题的所有案例有点意外,当时一直没思路,就直接return了数组长度减一,结果100%通过哈哈哈哈。
这题与区间合并也有点类似,就是求最多有多少个不相交的区间个数,上面几题区间是可以相交的。该题其实就是lc上的原题:

无重叠区间

该题可以用dp求解,跟俄罗斯套娃、最长递增子序列dp方法求解一样,但会超时!
因此采用第二种方法,核心思路就是:

1. 按照结束时间从小到大排序,然后对新列表进行遍历;
2. 判断当前区间是否满足:开始时间晚于或者等于上一次的结束时间;
3. 每次都选结束时间最早的;
4. 每选一次更新一下结束时间.

这题思路与前面的相反,选择的是以结束时间进行排序从小到大排序!!!

# # DP[i]以i下标区间结尾的最大不重合区间个数
# class Solution:
#     def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
#         if not intervals: return 0
#         intervals.sort()
#         n = len(intervals)
#         dp = [1] * n 
#         for i in range(1, n):
#             for j in range(i):
#                 if intervals[i][0] >= intervals[j][1]:
#                     dp[i] = max(dp[i], dp[j] + 1)
#         return n - max(dp)


# 贪心
class Solution:
    def eraseOverlapIntervals(self, intervals):
        end, cnt = float('-inf'), 0
        for s, e in sorted(intervals, key=lambda x: x[1]):
            if s >= end:
                end = e
            else: 
                cnt += 1
        return cnt

与该题相似的题还有以下几题:

用最少数量的箭引爆气球

在这里插入图片描述

# 按照左坐标从小到大排序
class Solution:
    def findMinArrowShots(self, points: List[List[int]]) -> int:
        points.sort()
        n = len(points)
        res, e = n, points[0][1]
        for i in range(1, n):
            if points[i][0] <= e:
                res -= 1
                e = min(points[i][1], e)
            else:
                e = points[i][1]
        return res

# # 按照右坐标从小到大排序
class Solution:
    def findMinArrowShots(self, points: List[List[int]]) -> int:
        points.sort(key = lambda x : x[1])
        n = len(points)
        res, e = 1, points[0][1]
        for p in points:
            if p[0] > e:
                res += 1
                e = p[1]
        return res     

会议室II

给定一个会议时间安排的数组,每个会议时间都会包括开始和结束的时间 [[s1,e1],[s2,e2],…] (si < ei),为避免会议冲突,同时要考虑充分利用会议室资源,请你计算至少需要多少间会议室,才能满足这些会议安排。
示例 1:
输入: [[0, 30],[5, 10],[15, 20]]
输出: 2
示例 2:
输入: [[7,10],[2,4]]
输出: 1

参考讲解
这个问题有一个非常简单的处理思路,我们可以先将上述的区间在坐标轴上画好,然后通过垂直于x轴的线从左向右移动,移动的过程中,记录这根线和区间相交的最大交点个数,这个最大交点个数就是相交的最大区间个数。

算法的实现上我们有一个trick,我们遍历intervals中的每一项it,然后对于左边的坐标用[it[0], 1]表示(表示我们进入一个线段),右边的坐标用[it[1], -1]表示(表示我们退出了一个线段),然后将这些新得到的区间加入到一个tmp数组中,对这个数组排序,接着遍历这个数组,遍历的过程中累加我们建立的标记位(也就是前面建立的1和-1)记录累加的最大值即可。

代码如下:

class Solution:
    def minMeetingRooms(self, intervals):
        if not intervals:
            return 0
        tmp = sorted(x for i, j in intervals for x in [[i, 1], [j, -1]])
        res, n = 0, 0
        for i, v in tmp:
            n += v
            res = max(res, n)
        return res

另外还可以用优先队列进行求解:

  • 首先进行一下排序,然后用一个小顶堆,维护当前每个会议室的结束时间,然后当一个新的时间安排出现的时候,只需要判断一下是否需要新申请一个会议室,还是继续使用之前的会议室。
from heapq import *  
class Solution:
    def minMeetingRooms(self, intervals):
        intervals.sort()
        pq = []
        for i in range(len(intervals)):
            if pq and pq[0] <= intervals[i][0]:
                heappop(pq)
            heappush(pq, intervals[i][1])
        return len(pq)    	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值