雙周賽113。比較簡單的換根dp基本款,甚至寫起來比Q2還快。

題目

一個n節點的簡單有向圖,編號由0到n-1。若此圖無向,則形成一棵

輸入整數n,以及二維整數陣列edges,其中edges[i] = [ui, vi],代表一條有向邊從ui指向vi

一次反轉可以改變邊的方向,也就是將ui往vi,改成vi往ui

對於每個節點i,分別計算使i能夠前往其餘任意節點的最少反轉次數

回傳整數陣列answer其中answer[i]代表以節點i為起點,使得其餘所有節點可到達的最少反轉次數

解法

一棵樹,代表任意兩節點之間只存在唯一一條路徑。

若從節點i出發前往j,則路徑上的所有反向邊都需要反轉。答案求的就是以i為根節點的樹中,所存在的反向邊數量。

首先建圖,對於每條邊加入一個參數,用1代表正向,-1代表反向。
先以0為根做一次dfs,求出以0為根節點的樹中總共有多少反向邊。

維護陣列neg,其中neg[i]代表以i為根時的反向邊數量,再來第二次dfs。
對於i的子節點j來說,樹中的邊大致上都是相同的,只有(i, j)這條邊的方向受到改變。
若(i, j)是正向,則neg[j]會比neg[j]多一條反向;反之,若(i, j)是反向,則neg[j]會比neg[i]少一條反向。

時間複雜度O(n)。
空間複雜度O(n)。

class Solution:
    def minEdgeReversals(self, n: int, edges: List[List[int]]) -> List[int]:
        g=[[] for _ in range(n)]
        for a,b in edges:
            g[a].append([b,1]) # pos
            g[b].append([a,-1]) # neg
            
        def dfs(i,fa):
            cnt=0
            for j,dir in g[i]:
                if j==fa:
                    continue
                if dir==-1:
                    cnt+=1
                cnt+=dfs(j,i)
            return cnt
        
        neg=[0]*n
        neg[0]=dfs(0,-1)

        def dfs2(i,fa):
            for j,dir in g[i]:
                if j==fa:
                    continue
                neg[j]=neg[i]
                if dir==1:
                    neg[j]+=1
                else:
                    neg[j]-=1
                dfs2(j,i)
        
        dfs2(0,-1)
        
        return neg