Algorithm 图移动算法

Algorithm 图移动算法,algorithm,graph-algorithm,Algorithm,Graph Algorithm,我有一个有趣的图论问题。我得到了一个有n个节点和一组边的树T。当然,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 我不知道如何处理这个算法。是否有可能找到此最佳巡更,或者我们是否可以不直接找到此最小值

我有一个有趣的图论问题。我得到了一个有n个节点和一组边的树T。当然,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


我不知道如何处理这个算法。是否有可能找到此最佳巡更,或者我们是否可以不直接找到此最小值?

将与每条边的权重相对应的额外边添加到图形中。(即,如果a->b的权重为3,则图形应包括a和b之间的3条无向边连接)

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

欧拉轨迹可以关闭(如果开始==结束)或打开(如果开始!=结束)

如果所有节点的阶数均为偶数,则存在闭合轨迹

如果除2之外的所有节点都具有偶数度,则存在开放轨迹

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

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

一种方法是在树上执行深度优先搜索,并跟踪可以添加到每个子树的最小边数,以使其具有0、1或2个奇数度顶点。这在树中的节点数上应该是线性的

示例代码 此Python代码计算图形的最短步数。 (为了构造图,你应该把它看作是有根的图,并为每个边的边加边)

从集合导入defaultdict
D=默认DICT(列表)
D[1].追加((2,1))
D[2].追加((3,1))
D[3].追加((4,2))
D[4].追加((5,1))
D[4].追加((6,1))
BIGNUM=100000
课堂备忘录:
定义初始化(self,fn):
self.fn=fn
self.memo={}
定义调用(self,*args):
如果不是self.memo.has_键(args):
self.memo[args]=self.fn(*args)
返回self.memo[args]
@回忆
定义最小奇数(节点、奇数、奇数、k):
“”“返回奇数(=k)的最低成本
如果从已考虑的边到该节点的边数为奇数,则奇数为1。”“”
边=D[节点]
如果k==len(边):
没有更多的孩子需要考虑,也没有选择的余地。
如果为奇数:
如果num_odd==1,则返回0,否则返回BIGNUM
如果num_odd==0,则返回0,否则返回BIGNUM
#我们决定是否添加另一条边,以及有多少奇数顶点来自子树
dest,w0=边[k]
best=BIGNUM
对于[0,1]中的额外值:
w=w0+额外费用
对于范围内的次奇数(奇数+1):
best=min(最佳,w+min_奇数(dest,sub_奇数,w&1,0)+min_奇数(node,num_奇数-sub_奇数,(奇数+w)&1,k+1))
回报最好
根=1
打印最小值(最小奇数(根,2,0,0),最小奇数(根,0,0))

首先要注意的是,如果你有一个算法可以从给定的起始节点找到最佳的行走,你可以将它作为潜在的起始节点应用于每个节点,然后选择最佳的。第二件要注意的是,如果你有一些由循环组成的行走。这是一个在同一节点开始和结束的路径段。Than这些路径可以任意重新排序或反转,而不会影响行走的质量。这会导致递归(归纳)关于行走的推理,因为一棵树有一个属性,即每一条边都将树分割成两个不相交的部分。这太棒了。非常感谢!尽管添加最少数量的边以满足欧拉轨迹存在的条件是困难的。我仍在试着理解它。再次感谢你。很乐意帮助:)我发布的代码只是我初步尝试绘制一个解决方案。可能有比我在这里提出的更好的解释/实现搜索的方法了。@Peterderivez,我运行这个算法是为了一个更大的测试(n=10^3),它花费了相当长的时间,而不是线性的。。有可能解决这个问题吗?
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) )