309周赛&86双赛
前言
最近对自己太放纵了,属实是有点不太行。
接下来要上强度了!
309 是miHoYo冠名周赛
T1
思路
T1又是很简单的数组题,我们只需要判断两个元素出现位置中间隔的元素是否与给定的距离表相等即可。
而且哦,这种位置的选择,我们可以通过hash表的方式很容易的完成。尤其是前后位次关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution : def checkDistances (self, s: str , distance ): hashmap={} for i,j in enumerate (s): if j not in hashmap.keys(): hashmap[j]=i else : val=i-hashmap[j] if val-1 !=distance[ord (j)-ord ("a" )]: return False return True
这题是不是很像两数之和?
那我们干脆一起把86双的T1一起讲了吧
呐,这鬼题也是,判断连续的,我们只需要看这个值是否被记录过就好了
1 2 3 4 5 6 7 8 9 10 11 class Solution : def findSubarrays (self, nums: List [int ] ) -> bool : hashmap={} for i in range (len (nums)-1 ): val=nums[i]+nums[i+1 ] if val in hashmap.keys(): return True hashmap[val]=1 return False
T2
思路
关键词:方法数!想到了什么?爬楼梯?斐波那契数列?没错,这种方法数,一般都有递推公式。这题也不例外。考虑两个方向,于是每次dp就能从左和从右。由于负数轴需要映射,为了简化操作,我们可以不用数组。
1 2 3 4 5 6 7 8 9 10 class Solution : def numberOfWays (self, startPos: int , endPos: int , k: int ) -> int : MOD = 10 ** 9 + 7 @cache def f (x: int , left: int ) -> int : if abs (x - endPos) > left: return 0 if left == 0 : return 1 return (f(x - 1 , left - 1 ) + f(x + 1 , left - 1 )) % MOD return f(startPos, k)
亦或是采用BFS进行广度搜索,得到所有可能的结果(相当于遍历,核心与DP相同)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 MOD = 10 **9 + 7 class Solution : def numberOfWays (self, startPos: int , endPos: int , k: int ) -> int : pre = defaultdict() pre[startPos] = 1 while k: nex = defaultdict() for pos in pre: nex[pos + 1 ] += pre[pos] nex[pos - 1 ] += pre[pos] for pos in nex: nex[pos] %= MOD pre = nex k -= 1 return pre[endPos] % MOD
解法二:数学!
正着走:X X X ,负着走K − X K-X K − X ,距离D D D
我们有:
X + ( − ( K − X ) ) = D X = D + K 2 X+(-(K-X))=D
\\
X=\frac{D+K}{2}
X + ( − ( K − X )) = D X = 2 D + K
于是,转换为求解:从K K K 中取X X X 个正数。
C K 1 / 2 ( D + K ) C^{1/2(D+K)}_K
C K 1/2 ( D + K )
那,关于Python如何求组合数,其实有一个内置函数comb
1 2 3 4 5 6 7 class Solution : def numberOfWays (self, startPos: int , endPos: int , k: int ) -> int : MOD=10 **9 +7 d=abs (endPos-startPos) if (k-abs (endPos-startPos))&1 or k<abs (endPos-startPos): return 0 return comb(k,(d+k)//2 )%MOD
不用Comb的话,我们可以这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def comb (m,n ): if m>n or type (m)!=int or type (n)!=int : return None return rank(n)//(rank(m)*rank(n-m)) def rank (n ): if n==0 or n==1 : return 1 ans=1 for i in range (2 ,n+1 ): ans*=i return int (ans) class Solution : def numberOfWays (self, startPos: int , endPos: int , k: int ) -> int : MOD=10 **9 +7 d=abs (endPos-startPos) if (k-abs (endPos-startPos))&1 or k<abs (endPos-startPos): return 0 return comb(k,(d+k)//2 )%MOD
T3
思路
位运算+双指针优化
其实看到这个连续就应该想到用双指针了。
当然,我的解法会遍历左右指针所有值,导致复杂度为:O ( n m ) O(nm) O ( nm )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution : def longestNiceSubarray (self, nums ) -> int : left=right=0 ans=0 n=len (nums) while right<n: i=breakPoint=left change=False while i<right: if nums[right]&nums[i]!=0 : breakPoint=i change=True i+=1 left=breakPoint+1 if change or breakPoint!=left else left ans=max (ans,len (nums[left:right+1 ])) right+=1 return ans
用位运算的话,复杂度上其实也差不多啦
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution : def longestNiceSubarray (self, nums ) -> int : left=right=0 ans=0 n=len (nums) or_=0 while right<n: while or_&nums[right]: or_^=nums[left] left+=1 or_|=nums[right] ans=max (ans,right-left+1 ) right+=1 return ans
当然,最后写的漂亮点,就是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution : def longestNiceSubarray (self, nums ) -> int : ans=left=or_=0 for right,x in enumerate (nums): while or_&x: or_^=nums[left] left+=1 or_|=x ans=max (ans,right-left+1 ) return ans
T4
思路
初见这个题的时候,想到的应该是贪心,谁有空就给谁,谁的时间最短谁就空闲。
于是我写了个不能AC的解:
超!!!!我这个解随便改改逻辑就AC了!!!!!!
超!!!!
我也做出T4了!!!!
这是离AK最近的一次!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class Solution : def mostBooked (self, n: int , meetings ): st=[[] for i in range (n)] val=[0 for i in range (n)] n=len (meetings) i=0 now=0 meetings.sort(key=lambda x:x[0 ]) change=[None ,None ] while i<n: if change[0 ]!=None : s,e=change else : s, e = meetings[i] if now<=s: now=s else : e+=now-s s=now for _ in st: if _ and _[0 ]<=now: _.pop() for l,j in enumerate (st): if j==[]: val[l]+=1 j.append(e) now=s i+=1 change[0 ]=None break else : now=min ([i[0 ] for i in st]) now=max (now,s) change=[now,now+(e-s)] check=val[0 ] idx=0 for i,j in enumerate (val): if j>check: check=j idx=i return idx
两个堆来回倒实现任务队列!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 from heapq import heappopfrom heapq import heappushclass Solution : def mostBooked (self, n: int , meetings ): cnt=[0 ]*n idle=list (range (n)) using=[] meetings.sort() for st,end in meetings: while using and using[0 ][0 ]<=st: heappush(idle,heappop(using)[1 ]) if len (idle)==0 : e,i=heappop(using) end+=e-st else : i=heappop(idle) cnt[i]+=1 heappush(using,(end,i)) ans=0 for i,c in enumerate (cnt): if c>cnt[ans]: ans=i return ans
O(n+mlogn+mlogn)
建堆+排+插入堆
86 双赛
T3和T4很有意思
T2
带余除法
当n > 4 , 2 < n − 2 n>4,2<n-2 n > 4 , 2 < n − 2 ,下面的表达式一定成立
n = q ∗ b + r , 0 < = r < b n=q*b+r,0<=r<b
n = q ∗ b + r , 0 <= r < b
这题完全就是脑筋急转弯
告诉你了,这玩意在n-2进制上必定为12
T3
这题不难看出是位运算,难得是如何进行。
不是没有想过枚举,但是会不会有一种更加高效的手段拿来实现这个算法?很可惜,似乎是没有的。
所幸的是,位运算枚举还是挺快的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution : def maximumRows (self, mat: List [List [int ]], cols: int ) -> int : ''' 位运算 枚举所有选择情况 实际上,二进制数就能表征所有的选择情况 二进制数还能用来表征更高位的数据 譬如之前的毒药和小鼠 ''' ans=0 mask=[sum (v<<j for j,v in enumerate (row)) for row in mat] for set in range (1 <<len (mat[0 ])): if set .bit_count()!=cols: continue cnt=0 for row in mask: if row &set ==row: cnt+=1 ans=max (ans,cnt) return ans
T4
这题的关键字:连续consecutive
所以,双指针搭建滑动窗口不失为一种好的选择。
当然,这类似背包问题,是存在一个限制条件的。
我们可以用单调队列来处理这种情况,通过维护单调队列,及时清理无用数据,保证队首是最大元素的同时,考虑是否加入和剔除元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 class Solution : def maximumRobots (self, chargeTimes: List [int ], runningCosts: List [int ], budget: int ) -> int : ans=s=left=0 q=deque() for right,(t,c) in enumerate (zip (chargeTimes,runningCosts)): while q and t>=chargeTimes[q[-1 ]]: q.pop() q.append(right) s+=c while q and chargeTimes[q[0 ]]+(right-left+1 )*s>budget: if q[0 ]==left: q.popleft() s-=runningCosts[left] left+=1 ans=max (ans,right-left+1 ) return ans
我们可以反思一下,什么时候可以用单调队列?
我们每次不符合条件的弹出都是最值
维护一个最值,使其满足限制条件
怎么使用单调队列?
在滑动窗口维护最值问题,可以考虑单调队列:
判断队列单调性,不满足时,队尾出队
增大窗口,队尾入队,更新统计值
判断是否满足条件,不满足则缩小窗口,队首出队