自下而上树形DP
学习内容
最大独立集
构建有向树,选择若干个点,使得相邻的两个点没有被选择,并且所选点的权重和最小
例题分析
状态变化
代码复现
#最大独立集
import sys
from collections import defaultdict
N = 100005
a = [0]*N
dp = [[0]*2 for _ in range(N)]
e = defaultdict(list)
def dfs(u):
for v in e[u]:
dfs(v)
#选u
dp[u][1] += dp[v][0]
#不选u max(dp[v])=>max(dp[v][0],dp[v][1])
dp[u][0] += max(dp[v])
dp[u][1] += a[u]
def main():
global N, a, dp, e
n = int(sys.stdin.readline())
#所有点的集合
st = set(range(1, n+1))
for i in range(1, n+1):
a[i] = int(sys.stdin.readline())
#y是x 的父节点
for _ in range(1, n):
x, y = map(int, sys.stdin.readline().split())
e[y].append(x)
#将x从集合中剔除
st.discard(x)
#剩下的一定是根节点
rt = st.pop()
dfs(rt)
print(max(dp[rt]))
if __name__ == "__main__":
main()
最小点覆盖
选择若干个点使得树上每一条边都被覆盖,即每一条边至少一个端点被选择,使得所选端点的权重和最小
例题分析
状态分析
代码复现
#最小点覆盖
import sys
from collections import defaultdict
N = 100005
dp = [[0]*2 for _ in range(N)]
e = defaultdict(list)
def dfs(u, fa):
for v in e[u]:
#等于父节点
if v == fa:
continue
dfs(v, u)
#不选u,所有子节点之和
dp[u][0] += dp[v][1]
#选u最小权重之和
dp[u][1] += min(dp[v])
#假设每一个点的权重都是1
dp[u][1] += 1
def main():
global N, dp, e
n = int(sys.stdin.readline())
#树等于无向图,也等于双向图
for _ in range(1, n):
x, y = map(int, sys.stdin.readline().split())
e[x].append(y)
e[y].append(x)
dfs(1, 0)
print(min(dp[1]))
if __name__ == "__main__":
main()
最小支配集
选择若干个点,使得树上的每一个点都被支配,即一个点要么被选中,要么他的邻居节点被选中,求点的权重和最小值
例题分析
状态分析
答案:ans= min(df[root][0],df[root][1])
代码复现
#最小支配集
import sys
from collections import defaultdict
N = 100005
a = [0]*N
dp = [[0]*3 for _ in range(N)]
e = defaultdict(list)
d = [0]*N
def dfs(u):
minn = float('inf')
for v in e[u]:
dfs(v)
# 被支配,已选
dp[u][0] += min(dp[v])
#被支配,未选
dp[u][1] += min(dp[v][0], dp[v][1])
#这里避免都没有选择子节点,先提前指定一个子节点
minn = min(minn, dp[v][0] - min(dp[v][0], dp[v][1]))
#所有点被支配,但是自己和子节点都没有选,父节点选了
dp[u][2] += dp[v][1]
dp[u][0] += a[u]
dp[u][1] += minn
def main():
global N, a, dp, e, d
n = int(sys.stdin.readline())
for _ in range(n):
id, a[id], m = map(int, sys.stdin.readline().split())
while m > 0:
x = int(sys.stdin.readline())
e[id].append(x)
#计算入度
d[x] += 1
m -= 1
#这里可以理解为入度为0的就是根节点
rt = d.index(0)
dfs(rt)
print(min(dp[rt][0], dp[rt][1]))
if __name__ == "__main__":
main()
感悟
这一次的课程看了好多遍,有点难理解但是可以多画图,多举例子,首先考虑的就是每个节点状态的多样性,将节点状态表示出来,然后再分析状态转移!
蓝桥杯云课学习笔记分享,欢迎大佬们批评指正!
一直在进步就好咯!
by 闻不多