LeetCode 2397. Maximum Rows Covered by Columns
雙周賽86。這題目描述真的超級爛,看半天才搞懂意思。除此之外本身倒是沒什麼難度。
題目
輸入m*n的二進制矩陣mat和一個整數cols,代表你必須選擇其中cols個不同的行數。
對於第r列來說,其中所有值為1的行數都有被選中,則稱這列被覆蓋。
求選擇cols行的形況下,最多可以覆蓋多少列。
解法
列數m和行數n最大都只有到12,回溯法生成所有選擇cols行的組合,複雜度為O(12!),還算可行。
檢查m列是否被覆蓋,每檢查一列的複雜度為行數,也就是O(n),總共是O(m*n)。
複雜度上限為O(12!*12*12),但其實需要檢查到的組合也沒這麼多,實際上應該差不多O(n!)。
維護一個函數bt做回溯,從所有行數中挑cols後,使用檢查函數check來確認各列是否被覆蓋。統計覆蓋列數cnt後更新答案。
這裡使用到set來保存被選擇的行數,所以check函數只要確保值為1的列數存在於列數集合中。
class Solution:
def maximumRows(self, mat: List[List[int]], cols: int) -> int:
M,N=len(mat),len(mat[0])
ans=0
def check(row,chosen):
for i,n in enumerate(row):
if n==1 and i not in chosen:return False
return True
def bt(i,chosen):
nonlocal ans
if i==N:
if len(chosen)==cols:
cnt=0
for row in mat:
if check(row,chosen):cnt+=1
ans=max(ans,cnt)
return
bt(i+1,chosen)
chosen.add(i)
bt(i+1,chosen)
chosen.remove(i)
bt(0,set())
return ans
另外一種思考方向,是每個行選或不選的組合,這時候就可以用到bitmask,若某行被選中則將其對應的位元設為1。
N個行可以產生2^N種組合,複雜度O(2^N)。
同時也需要各列轉換為bitmask,複雜度為O(M*N)。
之後遍歷所有選擇行數的組合comb,只處理正好cols行的的選法:
- 找出被覆蓋的row,若row的所有1位元都被選到,則row與comb做AND運算會保持原值
- 以被覆蓋的列數,更新答案
對於每個組合需要檢查M列,每次複雜度O(1)。整體複雜度為O(M*2^N)。
class Solution:
def maximumRows(self, mat: List[List[int]], cols: int) -> int:
M,N=len(mat),len(mat[0])
ans=0
rows=[]
for row in mat:
mask=0
for i,n in enumerate(row):
if n==1:mask|=(1<<i)
rows.append(mask)
# 0 ~ (i<<N)-1, total 2^N combs
for comb in range(1<<N):
# if comb.bit_count()==cols:
if bin(comb).count('1')==cols:
cnt=0
for row in rows:
if row&comb==row:cnt+=1
ans=max(ans,cnt)
return ans