树状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))
总结
后期会不定期更新更多题解。