簡體   English   中英

給定一個數字 k 和一個圖表是否有一個 DFS 運行將使森林大於 k

[英]given a number k and a graph is there a DFS run that will give forest larger then k

我收到了一個我似乎無法解決的問題。

給定一個有向圖 G=(V,E) 和一個自然數 k,k>0。

如果 DFS 運行 DFS 森林中的樹數 >= K,則找到一個返回“YES”的算法。

該算法在圖 G 的大小上應該是線性的。O(|V| + |E|)。

我會解釋:

因此對於以下運行,其中 s 是起始節點,我們將獲得 2 棵樹: 在此處輸入圖像描述

但是如果我們盯着節點 u 我們只會得到一棵樹。 所以我需要為 k = 1 或 2 返回 yes。沒有別的。

那么我怎么知道樹的數量呢?

謝謝您的幫助!

在對問題進行編輯后:

使用Kosaraju 算法在 O(V + E) 時間內得到強連通分量。 這將給出最大 K。

這里 max K 與強連通分量的數量相同。

為什么?

現在讓我們用反證法來證明。 假設問題中顯示的圖表有 4 個強連通分量。 假設有可能從某個節點v開始獲得額外的 dfs 樹。 這意味着要么在計算強連接組件的數量時未覆蓋節點v ,要么在 DFS 期間錯過了節點。 但是,如果我們進行 DFS 或使用經過充分驗證的算法找到強連通分量,則任何一種情況都是不可能的。 因此,我們的假設是錯誤的。 於是,反證法。


在編輯問題之前回答:

DFS(Vertex v):
    mark v as visited;
    for(Vertex neighbor: Neighbors of v){
        if(!isVisited(neighbor)){
            DFS(neighbor);
        })
    }

count_trees(Graph G): //V vertices and E edges in total
    for(Vertex v: V){
        if(!isVisited(v)){
            DFS(v);
            trees++;
        })
    }
    return trees;

以上步驟是不言自明的。 維護一個頂點是否被訪問是微不足道的。

上面的方法只是在之前沒有訪問過的每個節點上進行 DFS。 因此,時間復雜度與 DFS 相同,為O(|V| + |E|)

想象給定的圖實際上是一棵樹。 然后,如果您在樹的根部啟動一個 DFS,您將在一次 DFS 搜索中找到整個圖。 在另一個極端,如果你在一個葉子中啟動一個 DFS,然后在下一個葉子中,並在節點中啟動每個新的 DFS,就像你讓它們自下而上一樣,那么每個 DFS 將只找到一個節點,然后退出(因為之前的 DFS 已經訪問過孩子)。 因此,您可以啟動與樹中的節點一樣多的 DFS 搜索。

如果圖有一些額外的邊,同樣如此,但仍然是非循環的。

當圖形有循環時,它會變得不同。 在這種情況下,從循環的任何成員開始的 DFS 將找到該循環中的所有其他成員。 因此,一個循環永遠不會被不同的 DFS 搜索分割。 這個循環,加上與之相交的所有其他循環,就是所謂的強連通分量

因此,算法必須找到強連接的組件並將其計為 1 個 DFS,而所有其他不參與任何循環的節點都可以計為一個單獨的 DFS(因為您將從這些子樹的葉子開始)。

這是一個使用 DFS 的算法(這很令人困惑,因為它是一個 DFS 計算可能的 DFS)來識別周期並相應地更新計數。 我已經為這個算法使用了遞歸,因此當達到所需的k時必須有一些快速回溯:在這種情況下不需要進一步搜索。

所有邊只訪問一次,主循環也只訪問所有節點一次,因此達到了所需的時間復雜度。

def k_forests(adj, k):
    # pathindex[node] == 0: means node has not been visited
    # pathindex[node] == -1: means node has been visited and all neighbors processed
    # pathindex[node] > 0: means node is at this step in the current DFS path
    pathindex = [0] * len(adj) # none of the nodes has been visited

    def recur(node, depth):
        nonlocal k  # we will decrease this count

        if pathindex[node] > 0: # cycle back
            return pathindex[node]
        if pathindex[node] == -1: # already processed
            return depth
        pathindex[node] = depth # being visited
        cycle = depth + 1 # infinity
        for neighbor in adj[node]:
            cycle = min(cycle, recur(neighbor, depth + 1))
            if k == 0: # success
                return -1 # backtrack completely...
        if cycle >= depth: # no cylce detected or back out of cycle
            k -= 1
            if k == 0:
                return -1 # success
        pathindex[node] = -1 # completely visited and processed
        return cycle

    # main loop over the nodes
    for node in range(len(adj)):
        recur(node, 1)
        if k == 0:
            return "YES"
    return "NO"

調用這個function應該每個節點都有一個鄰接表,其中節點是用一個序號標識的,從0開始。例如問題中的圖可以表示如下,其中s=0,t=1,u =2,v=3,w=4,x=5,y=6,z=7:

adj = [
    [4, 7],
    [2, 3],
    [3, 1],
    [0, 4],
    [5],
    [7],
    [5],
    [6, 4]
]

print(k_forests(adj, 4)) #  YES
print(k_forests(adj, 5)) #  NO

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM