树状dp刷题总结

本文介绍了两道笔试题,分别是华为的获取最多食物问题和携程的树上染色问题,两者都涉及树形动态规划(树状DP)的应用。在获取最多食物的问题中,参与者在树形结构中移动,目标是最大化获得的食物总量。在树上染色问题中,目标是选择无交集的边染色,使边权和最大化。解题策略包括定义节点结束时的最大值状态并进行深度优先搜索来求解最大值。
摘要由CSDN通过智能技术生成

树状dp刷题总结



1.获取最多食物(华为2023.4.12笔试题)

1.1题目描述

主办方设计了一个获取食物的游戏。游戏的地图由N个方格组成,每个方格上至多2个传送门,通过传送门可将参与者传送至指定的其它方格。同时,每个方格上标注了三个数字: (1) 第一个数字id:代表方格的编号,从0到N-1,每个方格各不相同
(2)第二个数字parent-id:代表从编号为parent-id的方格可以通过传送门传送到当前方格(-1则表示没有任何方格可以通过传送门传送到此方格,这样的方格在地图中有且仅有一个);
(3)第三个数字value: 取值在[100,100]的整数值,正整数代表参与者得到相队取值单位的食物,负整数代表失去相应数值单位的食物(参与者可能存在临时持有食物为负数的情况),0则代表无变化。此外,地图设计时保证了参与者不可能到达相同的方格两次,并且至少有一个方格的value是正整数。游戏开始后,参与者任意选择一个方格作为出发点,当遇到下列情况之一退出游戏: (1)参与者当前所处的方格无传送门: (2) 参与者在任意方格上丰动宣布退出游戏 请计算参与者退出游戏后,最多可以获得多少单位的食物 解答要求 时间限制: C/C++ 1300ms.其他语言:2600ms内存限制: C/C++256MB其他语言:512MB 第一行:方块个数N (N<10000)

1.2输入示例

样例1输入:

7
0 1 8
1 -1 -2
2 1 9
4 0 -2
5 4 3
3 0 -3
6 2 -3

样例1输出:

9

解释:

参与者从方格0出发,通过传送门到达方格4,再通过传送门到达方格5。一共获得8+(-2) +3=9个单位食物,得到食物展多: 或者参与者在游戏开始时处于方格2,直接主动宣布退出游戏,也可以获得9个单位食物。

样例2输入:

3
0 -1 3
1 0 1
2 0 2

样例2输出:

5

解释:

参与者从方格0出发,通过传送门到达方格2,一共可以获得3+2=5个单位食物,此时得到食物最多。

1.3思路

树形dp。

我们定义dp[i]表示以节点i结尾,可以获取的最大食物的数量。

对于dp[i],我们的选择有走到父节点和不走到父节点,我们取最大的即可。也就是

dp[i] = max(当前节点的食物, 当前节点的食物 + dp[parent_id])

我们需要把所有的dp[i]都枚举一次,最终复杂度为O(n)

1.4代码

python:

n = int(input())
nodes = [[0,0]for _ in range(n)]
for i in range(n):
    id,pid,val = map(int, input().split(" "))
    nodes[id][0] = pid
    nodes[id][1] = val

'''以f(i)结尾的最大值'''
dp = [-float('inf') for _ in range(n)]
def dfs(i):
    if dp[i] != -float('inf'): return dp[i]
    if nodes[i][0] == -1: return nodes[i][1]
    dp[i] = max(nodes[i][1], nodes[i][1] + dfs(nodes[i][0]))
    return dp[i]

for i in range(n):
    dfs(i)

print(max(dp))

2.树上的染色(携程2023笔试题)

1.1题目描述

游游拿到一棵树,树的每条边有边权。
游游准备选择一些边染成红色,她希望不存在两条染红的边共用同一个点,且最终染红边的边权之和尽可能大。你能帮帮她吗?
注:所谓树,即不包含重边、自环和回路的无向连通图。
输入描述:
第一行输入一个正整数n,代表节点的数量。
接下来n-1行,每行输入三个正整数u,v,w,代表点u和点v之间有一条权值为w的无向边。
1 < = n < = 1 0 5 1<=n<=10^5 1<=n<=105
1 < = u , v < = n 1<=u,v<=n 1<=u,v<=n
1 < = w < = 1 0 9 1<=w<=10^9 1<=w<=109
输出描述:
一个正整数,代表最终染红的边的权值之和的最大值。

1.2输入示例

样例1输入:

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

样例1输出:

6

1.3思路

树形dp。

对于每个节点来说,我们可能的选择有2种,选择以这个节点为端点的其中一条边,又或者不选,但是这两种选择会影响到孩子节点的选择。
状态定义: d p [ n o d e ] [ 0 ] 为可以选择以 n o d e 为端点的一条边所获取的最大权值 ; d p [ n o d e ] [ 1 ] 为不选择以 n o d e 为端点的一条边所获取的最大权值。 dp[node][0] 为可以选择以node为端点的一条边所获取的最大权值; dp[node][1] 为不选择以node为端点的一条边所获取的最大权值。 dp[node][0]为可以选择以node为端点的一条边所获取的最大权值;dp[node][1]为不选择以node为端点的一条边所获取的最大权值。
状态转移: d p [ n o d e ] [ 0 ] = Σ d p [ c h i l d ] [ 1 ] dp[node][0] = Σ dp[child][1] dp[node][0]=Σdp[child][1]
d p [ n o d e ] [ 1 ] = M A X ( d p [ c h i l d i ] [ 0 ] + w + Σ d p [ c h i l d j ] [ 1 ] ) ,其中 j ! = i ,我们需要枚举 i dp[node][1] = MAX(dp[child_i][0] + w + Σdp[child_j][1]),其中 j != i,我们需要枚举i dp[node][1]=MAX(dp[childi][0]+w+Σdp[childj][1]),其中j!=i,我们需要枚举i

1.4代码

python:

n = int(input())
nxs = {i:[] for i in range(n + 1)}

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

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

#s:状态码 0代表选择node与pre的连边,1代表不选  dfs返回node与pre在状态为s时的最大值
def dfs(node, pre, s):
    if dp[node][s] != -1: return dp[node][s]
    notchic = 0

    for nx, w in nxs[node]:
        if nx != pre:
            notchic += dfs(nx, node, 1)
    if s == 0:  return notchic

    cho = 0
    for i in range(len(nxs[node])):
        tmp = 0
        for j in range(len(nxs[node])):
            nx, w = nxs[node][j]
            if pre != nx:
                if j == i:
                    tmp += dfs(nx, node, 0) + w
                else:
                    tmp += dfs(nx, node, 1)
        cho = max(tmp, cho)
    dp[node][s] = max(cho, notchic)
    return dp[node][s]

print(dfs(1,-1,1))

总结

后期会不定期更新更多题解。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值