断网导致原来写的那么多答案全没了,博客园能不能实时保存草稿,醉。
https://leetcode.com/problems/shortest-subarray-with-sum-at-least-k/
给一个数组a,找和大于k的所有子数组中的最短的那个。
最近二分有点上头,因为的确很强大,两个考虑二分的情况(其实需要刻意去想想能不能使用二分才会想到,这会是个好习惯):
1.最优问题变判断问题。比如:行递增列递增的矩阵中找某个元素->行递增列递增的矩阵中找第k大元素
2.有相应的简单孪生问题。比如1中的例子,再比如:找长度大于k的最大和->找和大于k的最小长度。
本题也想用二分来做,发现不对,假如我们能判断对于大于等于L的最大和子数组是不是大于k,那么我二分将得到的是大于k的所有子数组中最“大”长度是多少,而非最小。
于是乎根据问题应该用单调队列。这个也是,单独想出来不容易,但是证明有效就简单多了。下面证明单调队列有效。
目前一个维持好的单调队列[a1,a2,...an],a[i-1]和a[i]之间那些不在的点都是比a[i]大的,对于这些比a[i]大的点是不会比a[i]取得更好的结果的,因为a[i]又小又排后,和后面的某个a[x]配对会使子数组和又大而长度又短,等于是这两个指标都会比中间pop出去的这些点好,中间这些点又丑又不上进怎么行,那么我们就已经可以让他们退出历史舞台了!
单调队列的合理性可以分两步进行:
1.目前一个维持好的单调队列是对于这个新值有效的。
2.新值进来后,仍然保持单调队列特性。
那么新来一个值怎么去从单调队列中找他最好的配对对象呢?二分法找小于a[-1]-k的最大值,此处代码应该背过。
于是乎就是n*log(n)咯。
优化一个算法永远比直接想到最优解简单一些,目前我们的算法哪里可以优化?
二分找到一个中间值x后,左边的你那些小值是没用的,因为他们只能和后来的匹配形成长度更大的子数组,这是没有用的。
代码:
import collectionsclass Solution: def shortestSubarray(self, A, K): """ :type A: List[int] :type K: int :rtype: int """ sm = [0]+[k[0] for k in [[0]] for i in A if k.append(k.pop() + i) or True] # mi = [k[0] for k in [[sm[0]]] for i in sm if k.append(min(k.pop(), i)) or True] def getlen(stk,ind): if len(stk)<=1 or stk[-1]-stk[0]<K: return None,None tar=stk[-1]-K l,r=0,len(stk)-1 while r-l>1: m=(r+l)//2 if stk[-1]-stk[m]>=K: l=m else: r=m ans=ind[-1]-ind[l] for i in range(l): ind.popleft() stk.popleft() return ans,l stack=collections.deque() ind=collections.deque() ans=len(sm)+1 for i,s in enumerate(sm): while len(stack)>0 and stack[-1]>=s: stack.pop() ind.pop() stack.append(s) ind.append(i) l,pl=getlen(stack,ind) ans=min(ans,l) if l!=None else ans if ans>len(sm): ans=-1 return ans s=Solution()print(s.shortestSubarray([-11,-15,76,41,-41,68,41,12,73,-8],50))
---恢复内容结束---
lc 862. Shortest Subarray with Sum at Least K
原文地址:https://www.cnblogs.com/waldenlake/p/10666193.html