lc 862. Shortest Subarray with Sum at Least K

断网导致原来写的那么多答案全没了,博客园能不能实时保存草稿,醉。

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 collections
class 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))

 

 

 

 

 

 

 

---恢复内容结束---

posted @ 2019-04-07 17:47  Cloud.9  阅读(210)  评论(0编辑  收藏  举报