周賽322。坐牢坐牢的一天,雖然知道要拆成數個連通圖來做BFS,但一直想不出如何決定從哪個節點開始。
答案非常有趣,希望讀者朋友先自己思考看看。

題目

輸入正整數n,代表一個擁有n節點的無向圖,節點編號分別為1~n。

還有二維整數陣列edges,其中edges[i] = [ai, bi],代表ai, bi之間存在一條雙向道路。
注意,輸入的無向圖可能不完全連通

試將此圖分割成m個群組,使得:

  • 每個節點都屬於其中一個群組
  • 對於每對連通的節點ai, bi來說,如果ai隸屬群組x,且bi隸屬群組y,那麼abs(x-y)等於1

求最多可以分割成多少群組。如果無法分割則回傳-1。

解法

有點類似上一題的2492. minimum score of a path between two cities,需將所有連通的節點分組之後再進行切割。

先寫一個dfs函數找出連通的圖,放到group陣列中,再寫一個bfs函數對每個連通圖進行分組切割,回傳切割完的數量。

但根據bfs起點不同,得到的層數可能會不同,例如:

以範例1來說
從節點5出發,分組為[5],[1],[2,4],[3,6]
從節點2出發,分組為[1],[5,2,4],[3,6]

那麼讀者朋友想到判斷最佳起點的方法了嗎?

答案就是:暴力法全部試一次!

因為節點最多只有500個,最差情況下所有節點都連通,全部走一次也只要500*500次運算,根本綽綽有餘。
將所有連通圖的最大切割出數加總起來就是本題答案。

時間複雜度為O(N*(N+M)),其中N為節點總數,M為邊總數。空間複雜度是O(N+M),為節點的visited陣列和建圖的儲存成本。

class Solution:
    def magnificentSets(self, n: int, edges: List[List[int]]) -> int:
        vis=[False]*(n+1)        
        ans=0
        g=defaultdict(list)
        for a,b in edges:
            g[a].append(b)
            g[b].append(a)
        
        def dfs(i):
            nodes.add(i)
            vis[i]=True
            for j in g[i]:
                if not vis[j]:
                    dfs(j)
        
        def bfs(i):
            vis2=[False]*(n+1)
            vis2[i]=True
            curr=set([i])
            lvl=0
            while curr:
                next=set()
                lvl+=1
                for i in curr:
                    for j in g[i]:
                        if j in curr:return inf
                        if not vis2[j]:
                            vis2[j]=True
                            next.add(j)
                curr=next
            return lvl      
        
        group=[]
        nodes=set()
        for i in range(1,n+1):
            if not vis[i]:
                nodes=set()
                dfs(i)
                group.append(nodes)
    
        ans=0
        for grp in group:
            mx=0
            for i in grp:
                mx=max(mx,bfs(i))
            ans+=mx
                
        return -1 if ans==inf else ans