【问题标题】:For every node of a tree, find the nearest ancestor node such that val[node] is coprime to val[ancestor]对于树的每个节点,找到最近的祖先节点,使得 val[node] 与 val[ancestor] 互质
【发布时间】:2021-03-23 19:33:10
【问题描述】:

对于较短的版本,只阅读紧跟粗体句子的段落,它会减少到只有 3 个段落。

问题陈述: 给定一棵树,其 N 个节点以节点 1 为根。每个节点都与一个值相关联。确定包含与当前节点值互质的值的最近祖先。 (注意是节点值,不是节点号)

这是我的算法:

将列表定义为: adj[ ] 是邻接列表(从用户获取输入时构造的列表列表), vis[ ] 表示是否访问了节点,children[ ] 是列表的列表存储每个节点的子节点(如果存在)。由于这是一棵树,我们将构造 adj[ ] 使得 adj[node] = nodechildren 列表。这有助于我们不必担心节点是否被访问。

创建一个列表 parent[ ] 来存储每个节点的父节点。这样做:

def search_parent(node):
        for i in adj[node] :
                parent[i] = node
                search_parent(i)

我们的主要算法是从节点 1 开始并将其标记为 ans[1] = -1,因为它不能有祖先。以 DFS 方式遍历节点。通过设置变量 v 和 while 循环来检查互质祖先,使得 if gcd(node, v) == 1 : ans[node] = v else make v = parent[v]。这样,我们检查 parent 是否互质,如果不是,我们检查 parent[parent] 是否互质,依此类推,直到达到基本情况。

主要问题的伪代码:

ans[1] = -1
parent[1] = 0
def dfs(root) :
        loop node in adj[root] :
                v = root
                while (5 > 0) :
                    if gcd(val[node],val[v]) == 1 :
                        ans[node] = v
                        dfs(node)
                    else :
                        v = parent[v]
                        if v == 0 :
                            ans[node] = -1
                            dfs(node)
               
            

如果我们选择字典父代而不是列表父代,则可以通过一个常数因子降低代码的复杂性。然后当达到 v = parent[1] 时,我们可以直接让 parent[1] = -1 并且在 while 循环的下一步返回 ans[node] = -1,然后 while 循环终止。另一方面,当前代码对每个节点都经历了 if 条件直到 O(depth(node)) 次。

可以在 O(log_2 max(val[node])) 时间内评估 GCD。 while 循环运行的时间与 O(depth(node)) 成正比。假设 b 是图的最大分支因子。然后,整体复杂度将为 O(|V| + |E| + sum(b^{r O(N log_2 max(val)) 。

1.是否有更优化的代码(平均时间/空间复杂度)?

2。算法是否正确,或者逻辑中存在漏洞,或者在某些边界情况下可能存在?

【问题讨论】:

  • 1.可能值的范围是多少?如果它是较小的正整数,那可能是一种策略。如果它可能是巨大的/无限的,那么可能需要其他东西。 2. 对于给定的固定大小的已知树,这是一次操作吗?或者,即使在树中添加和删除成员或某些节点中的值发生更改时,您是否希望保持质量? 3. 树的预期大小是多少? N有多小?它有时/经常/总是很大吗? 4. 如果树或其值随时间变化,是否可以为每个节点存储额外的中间信息?
  • @Eric Arbitrary 是您所有问题的答案。
  • 可能更适合math.stackexchange.com
  • 如果不使用vis[],为什么要定义它?
  • adj[]children[] 有什么区别 - 后者没有被使用?

标签: algorithm graph tree depth-first-search tree-traversal


【解决方案1】:

我把你的算法变成了 Python,这样我就可以运行一些测试用例了:

from math import gcd

# Example Tree:
# Node numbers     Node values
# (array index or
# dictionary key)
#     0                1
#    / \              / \
#   1   2            7   1
#  / \   \          / \   \
# 3   4   5        14 14   2
#        / \              / \
#       6   7            6   6
adj = [
        [1, 2], # 0
        [3, 4], # 1
        [5],    # 2
        [],     # 3
        [],     # 4
        [6, 7], # 5
        [],     # 6
        []      # 7
]

val = [1, 7, 1, 14, 14, 2, 6, 6]

parent = [-1, 0, 0, 0, 0, 0, 0, 0]

def search_parent(node):
    for i in adj[node]:
        parent[i] = node
        search_parent(i)

search_parent(0)

ans = [-1, 0, 0, 0, 0, 0, 0, 0]

def dfs(root):
    for node in adj[root]:
        a = None
        v = root
        while a is None:
            if gcd(val[node], val[v]) == 1: % coprime
                a = v
                break
            else: % not coprime
                v = parent[v]
                if v == 0:
                    a = -1
                break
        ans[node] = a

        if node != 0:
            dfs(node) # continuing dfs no matter whether it's coprime or not.

dfs(0)

print("Nodes:   {0}".format(list(range(0,8))))
print("Values:  {0}".format(val))
print("Parents: {0}".format(parent))
print("Answers: {0}".format(ans))

问题 2:正确性

所以关于问题 2(正确性),让我们考虑几个测试用例 - 虽然可能只是我不明白您的期望。

如果所有值都具有平凡值1,那么最接近的互质数始终是父代:

Nodes:   [0, 1, 2, 3, 4, 5, 6, 7]
Values:  [1, 1, 1, 1, 1, 1, 1, 1]
Parents: [-1, 0, 0, 1, 1, 2, 5, 5]
Answers: [-1, 0, 0, 1, 1, 2, 5, 5]

现在考虑不是父母而是祖父母是祖先的情况。在分支2 的情况下,这看起来是正确的,但对于分支1 来说,它的子节点互质为根节点,遇到特殊情况if v == 0: a = -1

Nodes:   [0, 1, 2, 3, 4, 5, 6, 7]
Values:  [1, 7, 1, 14, 14, 2, 6, 6]
Parents: [-1, 0, 0, 1, 1, 2, 5, 5]
Answers: [-1, 0, 0, -1, -1, 2, 2, 2]

我想你更希望在这里if v == 0: a = 0

Answers: [-1, 0, 0, 0, 0, 2, 2, 2]

但是,您最初想到的情况是根节点是互质的,其中if v == 0: a = -1 是正确的:

Nodes:   [0, 1, 2, 3, 4, 5, 6, 7]
Values:  [7, 7, 1, 14, 14, 2, 6, 6]
Parents: [-1, 0, 0, 1, 1, 2, 5, 5]
Answers: [-1, -1, 0, -1, -1, 2, 2, 2]

所以这两种情况可能需要区分。

问题 1:复杂性

关于复杂性,您似乎计算错了 - 我认为您不能将父查找的成本添加到深度优先搜索的成本中。因为您必须对每个节点进行父级查找。

问题是您将使用for 向下传递给后代,而使用while 向上传递给祖先。如果您想要任何改进,则必须选择一个方向,例如仅使用for 向下传递给后代,并计算中间值以传递给dfs 的调用。

但是,我认为您无法摆脱 while 循环,您只能在最好的情况下减少它。例如,不是递归所有祖先,您可以通过将node 添加到列表中,仅当val[v] % val[node] == 0 并从列表中删除v 如果val[node] % val[v] == 0 在递归调用dfs 之前从列表中删除v,而不是递归所有祖先。 /p>

可能不值得(虽然没有进行任何测试运行)。

【讨论】:

    猜你喜欢
    • 2010-09-17
    • 1970-01-01
    • 2022-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-19
    • 1970-01-01
    • 2020-03-31
    相关资源
    最近更新 更多