周賽308。其實算是很簡單的Q4,但我沒看出來是拓樸排序,在那邊貪心半天。賽後看到知道是拓樸排序就馬上寫出來了,好冤。

題目

輸入正整數k,以及兩個由1~k組成的二元陣列:

  • 大小為n的二維整數陣列rowConditions,其中rowConditions[i] = [abovei, belowi]
  • 大小為m的二維整數陣列colConditions,其中colConditions[i] = [lefti, righti]

你必須構建一個k*k矩陣,其中包含1~k的每個數字正好一次。其餘格子的值應為0。

該矩陣需滿足以下條件:

  • 對於0~n-1中的每個i,abovei的所在列必須嚴格在於belowi所在列的上方
  • 對於0~m-1中的每個i,lefti的所在行必須嚴格在於righti所在行的左方

回傳任何滿足條件的矩陣。如果沒有答案,則回傳空矩陣。

解法

行列的限制條件是獨立的,不會交互影響,所以可以分開處理。
仔細想想,right必須出現在left的右方,就像是選修B課程前必須先修完A課程,這就是拓樸排序的暗示吧。

拓樸排序可以在有向無環圖中找到合法的訪問順序。
首先需要遍歷所有的邊,建立有向圖g,並維護入度陣列indegree。當某個點入度為0時,代表先決條件已經符合,接下來可以訪問。

建完圖g後,我們找出所有入度0的點,加入佇列中進行bfs。每訪問節點i,先將i點加到輸出順序out中,再將其所有相鄰節點j入度減少一,若入度成為0則加入佇列。bfs結束後回傳out,即為各節點的訪問順序。

如果圖中某些點存在循環,則那些點不會被訪問到,自然不會出現在out中。
所以我們分別對行、列的限制做拓樸排序後,先檢查的到的順序是不是都為k。若否,則代表條件不合法, 直接回傳空矩陣。
最後只要建立各數字和出現索引的映射,填入矩陣中即可。

拓樸排序的時間複雜度是O(V+E),V是頂點數量,也就是k,而E是邊的數量。E的上限是10^4,而k是400。
主要成本是在於產生矩陣,要建立k*k的矩陣,所以整體複雜度應該是O(k^2)。

class Solution:
    def buildMatrix(self, k: int, rowConditions: List[List[int]], colConditions: List[List[int]]) -> List[List[int]]:
        
        def topology_sort(cond):
            indegree=[0]*(k+1)
            g=defaultdict(list)
            q=deque()
            out=[]
            
            for a,b in cond:
                g[a].append(b)
                indegree[b]+=1
            
            for i in range(1,k+1):
                if indegree[i]==0:q.append(i)   
        
            while q:
                i=q.popleft()
                out.append(i)
                for j in g[i]:
                    indegree[j]-=1
                    if indegree[j]==0:q.append(j)
            return out
        
        rows=topology_sort(rowConditions)
        cols=topology_sort(colConditions)
        
        if not len(rows)==len(cols)==k:
            return []
        
        row_mp={n:i for i,n in enumerate(rows)}
        col_mp={n:i for i,n in enumerate(cols)}
        mat=[[0]*k for _ in range(k)]
        
        for i in range(1,k+1):
            r=row_mp[i]
            c=col_mp[i]
            mat[r][c]=i
        
        return mat