📜  门| GATE-CS-2007 |第 60 题(1)

📅  最后修改于: 2023-12-03 14:58:27.576000             🧑  作者: Mango

题目描述

给定一个大小为$n$的数组$a$,找到所有大小为$k$的子数组中最大值的最小值。具体地,对于每个子数组$a_{i}\ldots a_{i+k-1}$,输出其中的最大值的最小值。

例如,给定数组$a={10, 20, 30, 50, 10, 70, 30}$,$k=3$,则输出为$20, 30, 50, 50, 30$。

编写一个函数find_minimum_max(a:List[int],k:int)->List[int]来解决上述问题。注意:只有元素数量大于或等于$k$的子数组才需要考虑。

示例输入输出

输入:

a=[10, 20, 30, 50, 10, 70, 30]
k=3
find_minimum_max(a,k)

输出:

[20, 30, 50, 50, 30]

解题思路

该问题可以使用二分搜索和单调队列来解决。

对于二分搜索解法,我们可以进行二分搜索答案x,并检查$a$中是否存在一个大小为$k$和最大值不小于$x$的子数组。为了检查这个条件,我们可以扫描$a$,并使用单调队列来找到最大值。具体地,我们维护一个下标递增的单调递减队列$q$,其中$q$存储了$a$中的下标,并且对于任意$i<j$,如果$a_i<a_j$,则$i$先于$j$被从$q$中弹出。这保证了$q$中的所有元素在$a$中的值都不小于其前面的元素。每当队列$q$达到大小$k$时,我们记录$q[0]$处的元素,然后弹出队首元素。

在遍历过程中,我们只需要检查最后得到的最小值是否大于等于$x$即可。

时间复杂度为$O(n\log w)$,其中$w$是$a$中的最大值和最小值之差。

代码实现

from typing import List

def find_minimum_max(a:List[int],k:int)->List[int]:
    def check(x:int)->bool:
        q=[]
        for i in range(n):
            while q and q[0]<i-k+1:
                q.pop(0)
            while q and a[q[-1]]>a[i]:
                q.pop()
            q.append(i)
            if i>=k-1 and a[q[0]]>=x:
                return True
        return False
    n=len(a)
    l,r=0,max(a)
    while l<r:
        mid=(l+r+1)//2
        if check(mid):
            l=mid
        else:
            r=mid-1
    ans=[]
    q=[]
    for i in range(n):
        while q and q[0]<i-k+1:
            q.pop(0)
        while q and a[q[-1]]>a[i]:
            q.pop()
        q.append(i)
        if i>=k-1 and a[q[0]]>=l:
            ans.append(a[q[0]])
    return ans