Algorithm 最小生成树(MST)算法

Algorithm 最小生成树(MST)算法,algorithm,graph,graph-algorithm,greedy,minimum-spanning-tree,Algorithm,Graph,Graph Algorithm,Greedy,Minimum Spanning Tree,我在一次采访中被问到以下问题,我无法找到有效的解决办法 问题是: 我们想要建立一个网络,我们得到了c个节点/城市和D个可能的边缘/道路连接。边是双向的,我们知道边的代价。边的代价可以表示为d[i,j],其表示边i-j的代价。注:并非所有c节点都可以直接相互连接(D是可能的边集) 现在,我们得到了一个无成本的k个潜在边/连接的列表。但是,您只能在k边列表中选择一条边来使用(例如获得免费资金在两个城市之间修建机场) 所以问题是。。。找到一组道路(和一个免费机场),以最大限度地降低在高效运行时建立

我在一次采访中被问到以下问题,我无法找到有效的解决办法

问题是:

  • 我们想要建立一个网络,我们得到了c个节点/城市和D个可能的边缘/道路连接。边是双向的,我们知道边的代价。边的代价可以表示为d[i,j],其表示边i-j的代价。注:并非所有c节点都可以直接相互连接(D是可能的边集)

  • 现在,我们得到了一个无成本的k个潜在边/连接的列表。但是,您只能在k边列表中选择一条边来使用(例如获得免费资金在两个城市之间修建机场)

所以问题是。。。找到一组道路(和一个免费机场),以最大限度地降低在高效运行时建立连接所有城市的网络所需的总成本。 简言之,解决一个最小生成树问题,但是你可以在一个k个潜在边的列表中选择一条边,而不需要任何代价。我不知道如何解决。。。我已经尝试寻找所有的生成树,以增加成本和选择最低的成本,但我仍然面临挑战,如何考虑一个自由边缘的K无源自由边的列表。我还试着找到D电位连接的MST,然后根据k中的选项进行调整以得到结果


谢谢你的帮助

首先生成一个MST。现在,如果添加一条自由边,将只创建一个循环。然后,您可以删除循环中最重的边,以获得更便宜的树

若要通过添加一条自由边来找到最好的树,您需要在MST中找到可以用自由边替换的最重边

您可以通过一次测试一条自由边来实现这一点:

  • 挑一条自由边
  • 查找树中相邻顶点的最低公共祖先(从任意根)
  • 记住自由边顶点之间路径上最重的边
  • 完成后,您知道要使用哪个自由边—它是与最重的树边关联的边,并且您知道它将替换哪个边

    为了使步骤(2)和(3)更快,您可以记住每个节点的深度,并像跳过列表一样将其连接到多个祖先。然后,您可以在O(log | V |)时间内完成这些步骤,导致O((| E |+k)log | V |的总体复杂性,这非常好

    编辑:更简单的方法

    仔细考虑一下,似乎有一个超级简单的方法来确定使用哪个空闲边缘和替换哪个MST边缘

    忽略k条可能的自由边,使用Kruskal算法从其他边构建MST,但修改通常的不相交集数据结构,如下所示:

    • 按大小或等级使用并集,但不使用路径压缩。然后,每个联合操作将只建立一个链接,并且需要O(logn)时间,并且所有路径长度最多为O(logn)长
    • 对于每个链接,请记住导致创建链接的边的索引

    然后,对于每个可能的自由边,可以沿着不相交集结构中的链接进行遍历,以准确地找出其端点在哪个点连接到相同的连接组件中。您将获得最后一条所需边的索引,即它将替换的边,具有最大替换目标索引的自由边是您应该使用的边。

    一个想法是将您最喜欢的MST算法视为一个黑盒,并在请求MST之前考虑更改图中的边。例如,您可以尝试以下方法:

    for each edge in the list of possible free edges:
        make the graph G' formed by setting that edge cost to 0.
        compute the MST of G'
    
    return the cheapest MST out of all the ones generated this way
    
    这种方法的运行时间是O(kT(m,n)),其中k是要测试的边数,T(m,n)是使用您最喜欢的黑盒算法计算MST的成本

    我们可以做得更好。下面是一个众所周知的问题:

    假设一个图G有一个MST,然后减少一些边{u,v}的代价。在新的图G中找到一个MST'

    有许多算法可以有效地解决这个问题。这里有一个:

    Run a DFS in T starting at u until you find v.
    If the heaviest edge on the path found this way costs more than {u, v}:
       Delete that edge.
       Add {u, v} to the spanning tree.
    Return the resulting tree T'.
    
    (证明这项工作很乏味但可行。)这将给出一个成本为O(T(m,n)+kn的算法,因为您将构建一个初始MST(时间T(m,n)),然后在一个有n个节点的树中运行k次DFS


    然而,如果您可以使用一些更高级的算法,这可能会得到进一步的改进。Demaine等人的论文“关于笛卡尔树和范围最小查询”表明,在O(n)时间内,可以预处理最小生成树,以便在时间O(1)中,在时间O(1)中,查询形式为“该树中节点u和v之间的路径上的最低成本边是什么?”时间O(1)。因此,您可以构建此结构,而不是使用DFS来查找u和v之间的瓶颈边缘,从而将整个运行时减少到O(T(m,n)+n+k)。假设T(m,n)非常低(最著名的界是O(mα(m)),其中α(m)是Ackermann反函数,并且对于可行域中的所有输入小于5),这是一个渐进的非常快速的算法

    对此不确定,但与其解决一个大问题,不如解决
    c
    常规问题。假设一个节点是一个机场,只需计算主网络。重复
    c
    次。愚蠢的问题,但是你知道一个简短的证明,通过切割循环中最重的边得到的树确实是一个MST吗?我一直试图找到一个简短的证据,这是一段时间,我所提出的一切运行在多个段落。@ TaMeTeTyPyfFF考虑重新启动Kruskal的边缘相同的顺序,除了新的边缘第一。删除边之后的边仍然是必需的,因为它们看到的似乎是不相交集的集合。仍然需要删除边之前的边,因为前面的边不连接其末端