【问题标题】:Graph travelling algorithm图旅行算法
【发布时间】:2012-11-25 17:17:56
【问题描述】:

我有一个有趣的图论问题。我得到一棵树 T,它有 n 个节点和一组边。当然,T 是无向的。每条边都有权重,表示它必须被访问多少次(至少)。我们正在使用边从一个节点到另一个节点,任务是找到满足上述条件所需的最少步骤。我可以从任何节点开始。

例如,这棵树(括号中的边权重):

1 - 2 (1)

2 - 3 (1)

3 - 4 (2)

4 - 5 (1)

4 - 6 (1)

我们需要 8 步才能走过这棵树。例如:1->2->3->4->3->4->5->4->6

我不知道如何处理这个算法。是否有可能找到这个最佳游览,或者我们不能直接找到这个最小数量?

【问题讨论】:

  • 首先要注意的是,如果你有一个算法可以从给定的起始节点找到最佳步行,你可以将它应用到每个节点作为潜在的起始节点,然后选择最好的。
  • 第二个要注意的是,如果你有一些由循环组成的步行。那是在同一节点开始和结束的路径段。这些路径可以任意重新排序或反转,而不会影响步行的质量。这导致了关于行走的递归(归纳)推理,因为一棵树具有一个属性,即每条边都将树分成两个不相交的组件。

标签: algorithm graph-algorithm


【解决方案1】:

根据每条边的权重向图表中添加额外的边。 (即如果 a->b 的权重为 3,那么您的图形应该包括 a 和 b 之间的 3 个无向边连接)。

那么你试图找到的就是这张图上的欧拉轨迹。

欧拉轨迹可以是封闭的(如果 start==end)或开放的(如果 start!=end)。

如果所有节点的度数相等,则存在闭合路径。

如果除了 2 之外的所有节点都具有偶数度,则存在开放路径。

可以使用 Fleury 算法找到路径(如果速度太慢,也存在更快的线性算法)。

如果您的图不满足欧拉轨迹的要求,则只需添加最少数量的额外边,直到满足为止。

这样做的一种方法是在树上执行深度优先搜索,并跟踪可以添加到每个子树的最小边数,以便它具有 0、1 或 2 个奇数度的顶点。这应该花费与树中节点数成线性关系的时间。

示例代码

此 Python 代码计算图的最短步数。 (要构建图,您应该将其视为有根图,并为远离根的每条边添加边)

from collections import defaultdict
D=defaultdict(list)
D[1].append((2,1))
D[2].append((3,1))
D[3].append((4,2))
D[4].append((5,1))
D[4].append((6,1))
BIGNUM=100000

class Memoize:
    def __init__(self, fn):
        self.fn = fn
        self.memo = {}
    def __call__(self, *args):
        if not self.memo.has_key(args):
            self.memo[args] = self.fn(*args)
        return self.memo[args]

@Memoize
def min_odd(node,num_odd,odd,k):
    """Return minimum cost for num_odd (<=2) odd vertices in subtree centred at node, and using only children >=k

    odd is 1 if we have an odd number of edges into this node from already considered edges."""
    edges=D[node]
    if k==len(edges):
        # No more children to consider, and no choices to make
        if odd:
            return 0 if num_odd==1 else BIGNUM
        return 0 if num_odd==0 else BIGNUM

    # We decide whether to add another edge, and how many of the odd vertices to have coming from the subtree
    dest,w0 = edges[k]
    best = BIGNUM
    for extra in [0,1]:
        w = w0+extra
        for sub_odd in range(num_odd+1):
            best = min(best, w + min_odd(dest,sub_odd,w&1,0) + min_odd(node,num_odd-sub_odd,(odd+w)&1,k+1) )

    return best

root = 1
print min( min_odd(root,2,0,0),min_odd(root,0,0,0) )

【讨论】:

  • 这太棒了。太感谢了!尽管添加最少边数以满足欧拉轨迹存在条件的部分是困难的。我仍在努力理解它。再次感谢您。
  • 很高兴为您提供帮助 :) 我发布的代码只是我草拟解决方案的初步尝试。可能有比我在这里想出的更好的方法来解释/实现搜索。
  • @PeterdeRivaz,我为更大的测试(n=10^3)运行了这个算法,它花费了相当多的时间,而不是线性的。有可能解决这个问题吗?
猜你喜欢
  • 2021-01-19
  • 2019-04-14
  • 1970-01-01
  • 2013-06-09
  • 2017-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多