python 基础知识点(蓝桥杯python科目个人复习计划45)

本文介绍了自上而下树形动态规划方法,用于处理涉及树状结构的动态规划问题,如背包问题。文章详细阐述了状态定义、递归函数、记忆化搜索的应用,并以背包问题为例,展示了如何在二叉树中找到路径和的最大值。
摘要由CSDN通过智能技术生成

今日复习内容:还是动态规划

今天我复习的是树形DP,也就是自上而下树形动态规划,这是一种解决背包问题的方法,通常用于处理涉及树状结构的动态规划问题。在这种方法中,问题被建模成一棵树,其中节点表示状态,边表示状态之间的转移。接下来是我自己的理解。

1.问题描述

自上而下树形动态规划常用于解决具有树状结构的问题,例如树上的动态规划问题或者涉及到递归结构的问题。在这类问题中,常需要在节点上进行一些操作,并根据子节点的结果进行状态转移。

2.状态定义

定义合适的状态是自上而下树形动态规划的关键。每个节点可以表示一个状态,状态中包括当前节点的信息以及从该节点出发的边。

3.递归函数

使用递归函数来模拟树的遍历过程。递归函数通常接收当前节点的状态作为参数,然后递归地调用子节点的状态。

4.记忆化搜索

为例避免重复计算,使用记忆化搜索来保存已经计算过的状态。在每个节点处记录计算结果,下次遇到相同状态时直接返回已保存的结果。

5.示例问题

以背包问题为例,树形结构可以表示物品之间的依赖关系。每个节点表示选择或不选择当前物品,边表示物品间的依赖关系。递归函数考虑当前节点的选择,并递归调用子节点的状态。

6.优点和试用场景

自上而下树形动态规划在解决树状结构问题时具有灵活性和直观性。它可以更自然地反映问题的本质,并减少不必要的计算。适用于问题可以自然地建模成树状结构的情况。

7.注意事项

需要小心处理边界条件,确保递归函数能够正确终止。同时,确保状态转移的过程符合问题的实际逻辑。


接下来,我举个例子,是我在学的过程中找的一个题。

问题描述:给定一棵二叉树,每个节点包含一个整数值。找到路径和的最大值。路径被定义为从某个起始节点到树中的任意节点的任何节点序列,该路径必须至少包含一个节点,且不需要经过根节点。

示例树

     1
    / \
  2   3
  / \
4   5

解题思路:

1.状态定义

在这个问题中,树中的每个节点表示一个状态。我们需要考虑包含该节点的最大路径和。我们还可以考虑从该节点开始并通过其子节点向下的最大路径和。

2.递归

我们可以定义一个递归函数maxPathSum(node),用于计算包含给定节点的最大路径和。该函数将计算左子节点和右子节点的最大路径和,并将它们加到节点的值中。我们还需要考虑仅包含节点本身的路径和的情况。

3.记忆化搜索

我们可以使用记忆化搜索来存储已经计算过的路径结果,这有助于避免多次计算相同的路径。

4.基本情况

当函数遇到叶子节点(没有子节点的节点)时,即为基本情况。在这种情况下,函数返回节点本身的值。

def maxPathSum(node):
    if node is None:
        return 0
    # 基本情况,叶子节点
    if node.left is None and node.right is None:
        return node.val
    # 计算左右子树的最大路径和
    left_sum = maxPathSum(node.left)
    right_sum = maxPathSum(node.right)
    # 将当前节点值加到左右子树的最大路径和中
    max_sum = max(node.val,node.val + left_sum,node.val + right_sum)
    #如果必要,更新全局最大路径和
    global max_path_sum
    max_path_sum = max(max_sum,max_path_sum)
    #返回包括当前节点的最大路径和
    return max_sum

解释:

对于给定的示例树,最大路径和为12,路径为4 -->  2 --> 1 --> 3。maxPathSum函数从根节点1开始,计算其左子节点2和右子节点3的最大路径和,最后,它将更新全局最大路径和为12。

好了,基本思想就是这样,接下来来做一个题。


题目描述:

给一棵有根树,每个点都有一 个权值ai,要选择一些点使得权值和最大,且一个点若被选择,其儿子不能被选择,问最大值。

思路:这个题只是多了一个限制。

参考答案:

from collections import defaultdict

n = int(input())
val = [0] + [int(x) for x in input().split()]
edges = defaultdict(list)
f = [[0, val[i]] for i in range(n + 1)]

def add_edge(from_node, to_node):
    edges[from_node].append(to_node)

def dfs(u, fa):
    for v in edges[u]:
        if v == fa:
            continue
        dfs(v, u)
        f[u][0] += max(f[v][0], f[v][1])
        f[u][1] += f[v][0]

for _ in range(n - 1):
    u, v = map(int, input().split())
    add_edge(u, v)
    add_edge(v, u)

dfs(1, 0)
print(max(f[1][0], f[1][1]))

 

我来记录一下这个代码我是怎么照着写出来的。(这是我个人观点啊)

这段代码实现了一个解决树形动态规划问题的算法,其目标是选择一些节点使得权值和最大,且如果选择了某个节点,其子节点就不能被选择。以下是我对代码的解释:

1.输入处理

 n = int(input()):输入树的节点数目

val = [0] + [int(x)  for x in input().split()]:输入每个节点的权值,其中val[i]表示第i个节点的权值。

2.构建树结构

edges = defaultdict(list):使用字典来存储树的边,键为节点编号,值为与该节点相邻的节点列表

def add_edge(from_node,to_node):edge[from_node].append(to_node):

定义一个函数add_edge,用于向树中添加边,将from_node到to_node的边添加到边列表中。

3.动态规划过程

f = [[0,val[i]] for i in range(n + 1)]:f[i]表示以节点i为根节点的子树的最大权值和,其中f[i][0]表示不选取节点i时的最大权值和。

def dfs(u,fa):定义了一个深度优先搜索函数dfs,用于计算以节点u为根节点的子树中的最大权值和,fa表示节点u的父节点。

for v in edges[u]:遍历节点u的相邻节点

if v == fa:continue:如果相邻节点v是u的父节点,则跳过

dfs(v,u):递归地调用dfs函数,计算以v为根节点的子树中的最大权值和

f[u][0] += max(f[v][0],f[v][1]):更新不选取节点u的最大权值和,选择节点u时,其子节点不能被选择,所以累加最大的子节点权值和。

f[u][1] += f[v][0]:更新选取节点u时的最大权值和,选择节点u时,其子节点可以不被选择,所以累加子节点不选取时的最大权值和。

4.建立树结构

for _ in range(n - 1):u,v = map(int,input().split()):通过n - 1条边的输入构建树结构,每条边连接两个节点u和v。

add_edge(u,v)和add_edge(v,u):分别将边(u,v)和(v,u)添加到树的边列表中。

5.计算结果

dfs(1,0):从根节点开始进行深度优先搜索,计算以根节点为根的子树中的最大权值和。

print(max(f[1][0],f[1][1])):输出根节点的最大权值和,即选择根节点和不选择根节点两种情况中的最大值。

这段代码利用了动态规划,在遍历树的过程中,逐步计算每个节点的最大权值和,最终得到整棵树的最大权值和。

OK,这篇就到这里了,下一篇继续!


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值