树形DP2【2024蓝桥杯0基础】-学习笔记

自下而上树形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 闻不多

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值