1. 程式人生 > >lc 862. Shortest Subarray with Sum at Least K

lc 862. Shortest Subarray with Sum at Least K

多少 deque 才會 矩陣 col cti 代碼 ack 大於等於

斷網導致原來寫的那麽多答案全沒了,博客園能不能實時保存草稿,醉。

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

---恢復內容結束---

lc 862. Shortest Subarray with Sum at Least K