📜  门| GATE CS 2021 |套装2 |问题13(1)

📅  最后修改于: 2023-12-03 15:42:12.624000             🧑  作者: Mango

门 | GATE CS 2021 |套装2 |问题13

这是一道GATE CS 2021的问题,题目涉及动态规划和树的遍历。

题目描述

给定一棵n个节点的二叉树,节点编号从1到n。每个节点上有一个整数值,你需要选择树中的一些节点,并从中构建一条路径,使得所有所选节点之间的距离和最小。假设两个节点的距离是它们在树上的边数。请你输出所有选中节点的值的和。

输入格式

输入的第一行包含一个整数n,表示二叉树节点的个数。

接下来的n行,每行包含一个整数表示该节点的权值。

然后在接下来的n-1行中,每行包含两个整数u和v,表示在节点u和节点v之间有一条边。

输出格式

输出一个整数,表示所有选中节点的值的和。

示例

输入:

5
1
2
3
4
5
1 2
1 3
2 4
2 5

输出:

13
解题思路

这是一道树形dp的题目,需要用到动态规划的思想。

首先,我们需要知道如何计算树中两个节点之间的距离。为了简化问题,我们先考虑如何求树的直径,即树中最远的两个点之间的距离。我们可以任选一棵树的一条边,假设这条边的两个端点分别是u和v,先求出树中从u出发最远可以到达的节点x,然后再从x出发,求出树中最远可以到达的节点y。那么,从u到v的路径就是树的直径。树的直径可以用dfs或者bfs求出。

接下来,我们考虑如何计算选中节点之间的距离和。我们定义dp[i][0]表示以i为根节点的子树中,如果不选i,且选取的节点中恰好有一个在i的子节点中的距离和。同时,dp[i][1]表示以i为根节点的子树中,如果选i,且选取的节点中至少有一个在i的子节点中的距离和。那么,对于节点i的每个子节点j,我们可以得到以下转移方程。

dp[i][0] = max(dp[j][0]+dis(i,j), dp[j][1]) dp[i][1] = dp[j][0]+val[i]+sum(dp[k][1]),其中k为除j以外的i的子节点

dis(i,j)表示节点i与节点j之间的距离,val[i]表示节点i的权值,sum(dp[k][1])表示对于节点i的每个子节点k,如果选k,且选取的节点中至少有一个在k的子节点中的距离和。

最终的答案就是dp[1][1],即在以1为根节点的子树中,如果选1,且选取的节点中至少有一个在1的子节点中的距离和。

代码实现

下面是python3的代码实现,其中tree是一个字典,表示树的结构。

def dfs1(u, fa):
    d[u] = 0
    for v in tree[u]:
        if v != fa:
            dfs1(v, u)
            if d[u] < d[v] + 1:
                d[u] = d[v] + 1
                f[u] = v

def dfs2(u, fa):
    for v in range(2):
        dp[u][v] = 0
    for v in tree[u]:
        if v != fa:
            dfs2(v, u)
            dp[u][0] += max(dp[v][0]+dis(u, v), dp[v][1])
            dp[u][1] += dp[v][0]
    dp[u][1] += val[u]
    for v in tree[u]:
        if v != fa:
            dp[u][1] += dp[v][1]

n = int(input())
val = [0]*n
for i in range(n):
    val[i] = int(input())
tree = {}
for i in range(n-1):
    u, v = map(int, input().split())
    if u not in tree:
        tree[u] = []
    if v not in tree:
        tree[v] = []
    tree[u].append(v)
    tree[v].append(u)
d = [0]*n
f = [0]*n
dfs1(0, -1)
u = d.index(max(d))
dfs1(u, -1)
dis = {}
for i in range(n):
    dis[i] = d[i]
dfs2(0, -1)
print(dp[0][1])

返回的结果需要按照markdown的格式进行表现,下面是对应的markdown代码片段。

``` python
def dfs1(u, fa):
    d[u] = 0
    for v in tree[u]:
        if v != fa:
            dfs1(v, u)
            if d[u] < d[v] + 1:
                d[u] = d[v] + 1
                f[u] = v

def dfs2(u, fa):
    for v in range(2):
        dp[u][v] = 0
    for v in tree[u]:
        if v != fa:
            dfs2(v, u)
            dp[u][0] += max(dp[v][0]+dis(u, v), dp[v][1])
            dp[u][1] += dp[v][0]
    dp[u][1] += val[u]
    for v in tree[u]:
        if v != fa:
            dp[u][1] += dp[v][1]

n = int(input())
val = [0]*n
for i in range(n):
    val[i] = int(input())
tree = {}
for i in range(n-1):
    u, v = map(int, input().split())
    if u not in tree:
        tree[u] = []
    if v not in tree:
        tree[v] = []
    tree[u].append(v)
    tree[v].append(u)
d = [0]*n
f = [0]*n
dfs1(0, -1)
u = d.index(max(d))
dfs1(u, -1)
dis = {}
for i in range(n):
    dis[i] = d[i]
dfs2(0, -1)
print(dp[0][1])