📜  门| GATE-CS-2004 |第 53 题(1)

📅  最后修改于: 2023-12-03 14:58:25.964000             🧑  作者: Mango

题目描述

给定一个带有n个节点的树。设计一个算法,为每个节点分配一个整数权值,以使节点的权值之和最大而不使任何两个相邻的节点具有相同的权值。树的节点编号从1到n。

输入格式

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

接下来n-1行,每行包含两个整数u和v,表示树中存在一条连接节点u和v的边。

输出格式

输出一个整数,表示最大权值和。

示例

输入:

4
1 2
1 3
3 4

输出:

4
解题思路

对于一棵树,我们可以设计一个dp状态来表示每个节点的权值。假设max_val[i]表示取到以节点i为根的子树时所选的最大权值,那么对于每个节点i,它的初始值应当为该节点的点权。

在从子节点向父节点递推时,考虑到我们需要满足选到相邻节点权值不同的限制,因此我们在递推时需要考虑两种情况。对于父节点p和儿子节点c,情况分别如下:

  1. 如果p不选择,那么子节点c可以选择或者不选择,我们可以获取两种情况下最大权值max_val_l和max_val_r,然后取两者的和作为在c不选择的情况下p可以获取到的最大权值。即$dp[p][0]=\sum_{i=1}^{k}{dp[c_i][1]}+\sum_{i=1}^{k}{w_p}$

  2. 如果p选择了,那么子节点c一定不能选择,我们只能获取儿子节点的儿子节点的权值之和。即$dp[p][1]=\sum_{i=1}^{k}{max(dp[c_i][0],dp[c_i][1])}$

最终的答案应当为以任何一个节点为根节点时的最大权值。

参考代码
n = int(input())

graph = [[] for _ in range(n + 1)]
for _ in range(n - 1):
    u, v = map(int, input().split())
    graph[u].append(v)
    graph[v].append(u)

w = [0] + list(map(int, input().split()))

dp = [[0] * 2 for _ in range(n + 1)]

def dfs(u, fa):
    dp[u][0] = sum(max(dp[v][0], dp[v][1]) for v in graph[u] if v != fa) + w[u]
    for v in graph[u]:
        if v != fa:
            dfs(v, u)
            dp[u][1] += max(dp[v][0], dp[v][1])

ans = 0
for i in range(1, n + 1):
    dfs(i, 0)
    ans = max(ans, max(dp[i]))

print(ans)

时间复杂度: $O(n)$