![](/img/trans.png)
[英]Given a weighted graph and natural number k how to find the cheapest path from node s to t that can be divided by k?
[英]given a number k and a graph is there a DFS run that will give forest larger then k
在对问题进行编辑后:
使用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.