动态规划(DP)的原理、实现及应用

C语言实现01背包问题与动态规划 本文还有配套的精品资源,点击获取 简介:01背包问题是一个典型的计算机算法问题,常用于资源分配和任务调度等领域。它通过动态规划的方法,优化处理重叠子问题和最优子结构的复杂问题,从而提高效率。问题涉及选择若干物品放入背包,在不超过背包容量的前提下,使物品总价值最大。本文通过构建二维数组dp[i][j]来记录最优解,使用C语言进行算法实现,并提供了解决方案的代码框架。 ... 阅读详情

文章目录

 

1. 由一个例子说开: 斐波那契(fibonacci)数列

斐波那契数列是由0和1开始,之后的数就是前两个数的和。
首几个费波那契系数是:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……
那我们如何用计算机来生成这些数呢,也就是说,求斐波那契数列第n位的值。
很简单,用递归就可以了:

# 自上而下地递归
def fib(n):
    if n in [0,1]:
        return n
    return fib(n-1) + fib(n-2)
    
print(fib(10))

输出: 55
  • 1

结果没有问题,接下来看一下递归性能如何。

性能测试

写个简单的计时的测试代码:

import time
n = 40
tic = time.clock()
res = fib(n)
toc = time.clock()
print('fib({}) = {}'.format(n,res))
print('runtime: {}s '.format(toc-tic))

我们看下n=10的结果:

fib(10) = 55
runtime: 2.4462208330078283e-05s 

再看下 n=40 时的结果:

fib(40) = 102334155
runtime: 40.73219172568648s 

我们发现:当n变大时,这段递归代码用时变得很久,而且性能是急剧下降。这是为什么呢?

原因分析

拿fib(5)来举例,要计算fib(5),得知道fib(4)的值,要计算fib(4)的值,又得知道fib(3)的值,以此类推,直到递归到底时,fib(0)=0。下图是个fib(5)求解的展开图,可以看到,这里面有很过的重复计算,比如浅粉色框框住的fib(2)就重复计算了三次,可想而知当n很大时,这些重复计算的次数将呈指数上升,这就是递归效率不高的原因——重叠子问题。
fib(5)示意图


2. 记忆化搜索

改进方法就是,将计算过的fib()值存储下来,下次用到直接查就行,这种方法叫记忆化搜索。
于是在递归代码的基础上可以很轻松地改写成记忆化搜索,代码如下:

# 记忆化搜索:自上而下地求解
def fib_memo(n):
    def fib(n):
        if n in [0,1]:
            return n
        # 如果memo数组中没有计算过fib(n),则需计算一下并存到memo[n]中
        if memo[n] == -1:
            memo[n] = fib(n-1) + fib(n-2)
        return memo[n]

    memo = [-1 for i in range(n+1)]  # memo[0...n]中所有元素初始值为-1,表示未计算
    return fib(n)

 

fib(40)的测试结果:

fib(40) = 102334155
runtime: 2.4746652798057767e-05s 

经过简单的记忆化改造,效率由 40.7s 提升到了 0.0000247s !
记忆化搜索本质还是一种递归,只是用额外的变量来存储中间值而已,属于典型的空间换时间的方式。由于递归是一种自顶向下的方式,所以记忆化搜索也是一种自顶而下的搜索方法,即计算顺序是

   fib(n) → fib(n-1) → fib(n-2) → ... → fib(1) → fib(0)

最后再从fib(0)一路回溯给fib(n),done!

那么可不可以自底向上地算出斐波那契数列呢?当然有啊,这就是接下来要隆重请出的动态规划。


3. 动态规划(Dynamic Programming,DP)

思路很简单,知道了 fib(0) 和 fib(1),就可以相加得fib(2),fib(1)+fib(2)又得fib(3),依次类推直到 fib(n)。自底向上的求解过程:

   fib(0) → fib(1) → fib(2) → ... → fib(n-1) → fib(n)

没错,这也极其符合人的直观感觉,看着很舒服很顺眼。
于是很容易写出代码:

# 动态规划:自下而上地解决问题
def fib(n):
    memo = [-1 for i in range(n+1)]
    memo[0] = 0
    memo[1] = 1
    for i in range(2,n+1):
        memo[i] = memo[i-1] + memo[i-2]
    
    return memo[n]

看下测试结果:

fib(40) = 102334155
runtime: 1.6782212696853094e-05s 

0.0000167s 比记忆化搜索的 0.0000247s 又提高了一丢丢,这主要是因为记忆化搜索中多出了反复递归的开销。

最优子结构

其实fib数列不是重点,重点是这个:这里面fib(2)是fib(3)的子问题,fib(3)又是fib(4)的子问题,以此类推,所以这是一个通过子问题推得原问题的过程。我们可以把上面的过程总结为这句话:通过求子问题的最优解,可以获得原问题的最优解。
这里的子问题称为最优子结构。
好,接下类引出动态规划的定义:

将原问题拆解成若干个子问题,同时保存子问题的答案,使得每个子问题只求解一次,最终获得原问题的答案。

好,介绍完毕。

总结一下这几个解法:

在递归问题中如果存在重叠子问题,那么就可以进行以下两种改造:

  1. 自顶而下地解决问题:记忆化搜索
  2. 自底而上地解决问题:动态规划
    动态规划

几个例题

LeetCode 70 Climbing Stairs

LeetCode 70. 爬楼梯
解析:要爬到n阶台阶,有 ①从n-1阶再爬一阶到达 ②也可从n-2阶再爬2阶到达,这两种方式,所以爬到n阶的方法数等于爬到n-1阶的方法数与爬到n-2的方法数的和。接下来,n-1阶的方法数也等于n-2阶的加上n-3阶的,以此类推直到爬1阶和爬0阶。所以,这个问题本质就是一个fibonacci数列的求解! 而且这里面也存在很多重复计算的子块,例如下图蓝色子块。同上面的代码一样,这个问题完全可以用原生的递归方法、记忆化搜索和动态规划求解的。
爬楼梯

LeetCode上 其他相似的例题:
在这里插入图片描述
在这里插入图片描述


4. 动态规划的核心:状态与状态转移方程

前面我们将动态规划描述为一种 通过求子问题的最优解,从而求得原问题的最优解 的方法,那么有人可能会问了:

  1. 如何将原问题切分成子问题?
  2. 又怎么将子问题的解延伸回原问题,也就是说子问题和原问题是怎么连接起来的?

对于问题1,如何切分子问题,在DP中被称为状态的定义,其中子问题被称为状态。
对于问题2:,如何从子问题到原问题,在DP被称为状态转移方程。

这两个东西才是动态规划的核心。

到底是怎么回事,直接拿LeetCode上两个例子说话:

LeetCode 343 Integer Break

LeetCode 343 Integer Break
解析:

  1. 我们定义 v[n] 分割正数n的最大乘积 ,这是状态的定义。
  2. 每次将n分解为两个数的和,如:1+(n-1), 2+(n-2), 3+(n-3)…,对于每一种分割 i+(n-i),最大乘积 v[n] 的来源于 i 和 n-i 的乘积 和 i 和 v[n-i] 的乘积 中较大的那个,而最终的最大v[n]是在所有 1<=i<=n上最大的那个,即v[n] = max( i×(n-i), i×v[n-i] ) for 1<=i<=n-1 ,这就是从v[n-1] 到 v[n] 的状态转移方程。
  3. 这里存在重叠子问题,如下图中蓝块。
    在这里插入图片描述
    思路: 递归、记忆化搜索、动态规划
  1. 状态定义: v[n] 分割正数n的最大乘积
  2. 状态转移: v[n] = max( i×(n-i), i×v[n-i] ) for 1<=i<=n-1

代码
我将递归、记忆化搜索和动态规划三种方法放在了一个类中:

class Solution:
    def integerBreak_rec(self, n):
        """
        :type n: int
        :rtype: int
        原生的递归方法:效率低
        """
        def breakInteger(n):
            if n==1:
                return 1
            res = -1
            for i in range(1,n):
                # n = i + (n-i)
                res = max( res, i*(n-i), i*breakInteger(n-i) )
            return res

        return breakInteger(n)

    def integerBreak_memo(self, n):
        """
        :type n: int
        :rtype: int
        第一种改进: 自上而下的记忆化搜索
        """
        def breakInteger(n):
            if n==1:
                return 1
            if memo[n] != -1:
                return memo[n]
            res = -1
            for i in range(1,n):
                # n = i + (n-i)
                res = max( res, i*(n-i), i*breakInteger(n-i) )
            memo[n] = res
            return res

        assert n>=2
        memo = [-1 for i in range(n+1)]
        return breakInteger(n)

    def integerBreak_dp(self, n):
        """
        :type n: int
        :rtype: int
        第二种改进:自底向上动态规划
        """
        assert n>=2
        memo = [-1 for i in range(n+1)]

        memo[1] = 1
        for i in range(2,n+1):
            for j in range(1,i):
                memo[i] = max( memo[i], j*(i-j), j*memo[i-j] )
                
        return memo[n]
     
if __name__ == '__main__':      
    n = Solution().integerBreak_dp(10)
    print(n)

LeetCode上与此题相似的题目:

  • 279 Perfect Squares
    给定一格正整数n,寻找最少的完全平方数,使他们的和为n。
  • 91 Decode Ways
    数字字符串的解析
  • 62 Unique Paths
    机器人从m×n的矩阵左上角出发到达右下角(每次只能向右或向下),共有多少种路径?
  • 63 Unique Paths II
    (接上题)若矩阵中含有障碍物,此时共有多少种路径?

LeetCode 198 House Robber

小偷洗劫一条街上的所有房子,每个房子有不同价值的把宝物,但不能连续抢劫俩相邻房子。求最多可以偷到多少宝物?

暴力解法

检查所有房子的组合,对每一个组合,检查是否有相邻的房子,如果没有,记录其价值,找最大值。
时间复杂度: O( (2^n)*n )
这个方法效率非常之低。

动态规划

考虑如下结构
在这里插入图片描述

"""
198. House Robber

转态定义:考虑偷取[x...n)范围里的房子
状态转移方程:
            f(0) = max{ v(0)+f(2),
                        v(1)+f(3),
                        v(2)+f(4),
                        ...
                        v(n-3)+f(n-1),
                        v(n-2),
                        v(n-1)}  
"""
class Solution:
    def rob_rec(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        递归方法
        """
        # 考虑抢劫nums[index:]范围内的所有房子
        def tryRob(index):
            if index >= len(nums):
                return 0
            res = 0
            for i in range(index,len(nums)):
                res = max( res, nums[i]+tryRob(i+2) )
            return res

        return tryRob(0)

    def rob_memo(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        记忆化搜索
        """
        # 考虑抢劫nums[index:]范围内的所有房子
        def tryRob(index):
            if index >= len(nums):
                return 0
            if memo[index] != -1:
                return memo[index]

            res = 0
            for i in range(index,len(nums)):
                res = max( res, nums[i]+tryRob(i+2) )
            memo[index] = res
            return res

        # memo[i] 表示考虑抢劫 nums[i...n) 所能获得的最大收益
        memo = [-1 for i in range(n)]
        return tryRob(0)

        
    def rob_dp(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        动态规划法
        """
        n = len(nums)
        if n == 0:
            return 0
        
        # memo[i] 表示考虑抢劫 nums[i...n) 所能获得的最大收益
        memo = [-1 for i in range(n)]
        memo[n-1] = nums[n-1]
        
        for i in range(n-2,-1,-1):
            # memo[i]
            for j in range(i,n):
                if j<n-2:
                    memo[i] = max(memo[i],nums[j]+(memo[j+2]) 
                else:
                    memo[i] = max(memo[i],nums[j]) # 末尾两个元素只能是自己
                
        return memo[0]
        
        
if __name__ == '__main__':          
	nums = [2,7,9,3,1]
	res = Solution().rob_dp(nums)   
	print(res)

LeetCode上与此题相似的题目:

  • 213 House Robber II
    此处将街道改为环形街道,即最后一个元素和第一个元素为邻居。求窃得财产的最大值。
  • 337 House Robber III
    此处将街道给为小区,小区是二叉树结构,即不能同时选择相邻节点。
  • 309 Best Time to Buy and Sell Stock with Cooldown
    股票买卖时机的选择,使利润最大化。

5. 动态规划经典应用:背包问题

在动态规划中,有类非常经典的问题称为0-1背包问题。有一个背包,容量是C,现有那种不同的物品,编号为0…n-1
,其中每一件物品重量为w(i),价值为v(i)。问可以向包中放哪些物品,使得在不超过背包容量的基础上,物品总价值最大。

三种解法

暴力解法:每一件物品都可以放进或者不放进,遍历所有可能性。 时间复杂度O((2^n)*n)。
贪心算法:优先放入平均价值最大的物品,结果只是近似解,而不是最优解。
动态规划:

状态: F(n,C) 考虑将n个物品放进容量为C的背包,使得价值最大。(这里约束条件为n和C)
状态转移方程: F(i,C) = max( F(i-1,C), v(i)+F(i-1,C-w(i)) )
对应 1.不放入第i个物品 2.放入第i个物品 这两种状态中值最大的那个。

背包问题动态规划解法的实现及优化

时间复杂度为O(nC)已经几乎没有优化空间了,但是可以从空间复杂度上进行优化。

  1. 动态规划解法: n行版
    原始动态规划,建立一个n行C+1列的矩阵 memo[n-1][C+1]
    时间复杂度:O(nC)
    空间复杂度:O(nC)

  2. 动态规划解法: 两行版
    改进1:由于第i行元素只依赖于第i-1行元素。理论上,memo只需要保证两行元素。
    空间复杂度 O(2C)=O( C)

  3. 动态规划解法: 一行版
    改进2:由于某位置的元素只依赖于上一行的对应位置和上一行的左侧元素。所以可以从右边开始更新,这样memo只需一行就可以完成。
    空间复杂度 O( C)

背包问题的变种

  • 多重背包问题:每个物品不止1个,有num(i)个
  • 完全背包问题:每个物品可以无限使用
  • 多维费用背包问题:要考虑物品的体积和重量两个维度(原问题上增加一个约束条件,memo数组变为三维即可)
  • 物品间加入更多约束:物品间可以相互排斥;也可以相互依赖

背包问题实例

416 Patition Equal Subset Sum 分割等和子集

典型的背包问题:在n个物品中选出一定物品,填满sum/2的背包
与原背包问题不同的是:我们不需要物品价值最大,目标是能填满。或者也可以理解为物品价值均为1。

LeetCode上与此题相似的题目:

  • 322 Coin Change
    给定不同面值的硬币,问需要多少硬币(可反复使用),可以凑成指定的金额?
  • 377 Combination Sum IV
    给定一整数数组(无重复数字,但数字可重复使用),问有多少种可能,使用这个数组中的数字,凑成一个指定的整数target。
  • 474 Ones and Zeros
  • 139 Word Break
    判断字符串数组中可否有不同字符串收尾相连组成target字符串。
  • 494 Target Sum
    给定非0数字数组,在这些数前面加上+或-,使其结果为给定的整数S。

6. 动态规划应用: 最长上升子序列(LIS)

300 Longest Increasing Subsequence

给定一整数序列,求其中的最长上升子序列的长度。

  • 方法1:动态规划解法
    1. 状态定义: LIS(i) 表示以第i个数字为结尾的最长上升子序列的长度。即表示[0…i]范围内,选择数组nums[i] 可以获得的最长上升子序列的长度
    2. 状态转移: LIS(i) = max( 1+LIS(j) if nums[i]>nums[j] for j<i )
    时间复杂度O(n^2)
  • 方法2:二分搜索法
    这里不做介绍。
    时间复杂度O(nlogn)

LeetCode上与此题相似的题目:

  • 376 Wiggle Subsequence
    求最长轮流交替子序列

最长公共子序列: LCS问题

给出两个字符串S1和S2,求这两个字符串的最长公共子序列的长度。
状态定义:

LCS(m,n) 是S1[0…m]和S2[0…n]的最长公共子序列的长度。

转移方程:

  1. S1[m] == S2[n]: LCS(m,n) = 1+ LCS(m-1,n-1)
  2. S1[m] != S2[n]: LCS(m,n) = max(LCS(m-1,n),LCS(m,n-1))

7. 关于动态规划的其他:

  • 动态规划其实相当常见,譬如 dijkstra单源最短路径算法也是动态规划

    状态定义: shortestPath(i)为从start到i的最短路径长度
    状态转移: shortestPath(i) = min( shortestPath(i) + w(a->i))

  • 动态规划是一个非常大的问题范畴,因为它不仅他可以解决的问题是非常灵活的,还因为它是公认的在算法设计上艺术含量非常高的算法,因为动态规划似乎没有一个固定的套路。
  • LeetCode上更多关于动态规划的问题,找dp标签的题。
  • 单论面试,面试中动态规划考察较少,更多的是考察更基础的问题,所以可以相对放松对LeetCode上动态规划题目的要求。

8. 如何用动态规划给出具体的解:返回去找

例1: 300. 最长上升子序列。
例2: 0-1背包问题

References:
非常好的动态规划总结,DP总结
背包问题的总结
动态规划(dp) 之 状态转移方程

动态规划算法原理及例子 动态规划思想原理及说明,其中有经典的例子:背包问题、最优三角.... 立即下载

相关推荐

手把手实战:快速构建合同智能审查Agent

配置“模型供应商”,选择 “OpenAI-API-compatible”,输入申请到的 “API Key” 和 “URL” 后点击“保存”,如下图示例完成 “AlayaNew/DeepSeek-R1。至此,我们完成了合同智能审核Agent的构建,我们也可以在Dify中把这个智能体发布为API服务,这样企业其它应用如OA就可以通过API集成,在原合同流程中实现智能审核工作。该节点“输入变量”中,配置从“开始”节点中获取上传的文件,输出变量就是合同的内容,配置如下图。接下来我们开始创建智能体。

weixin_46880696的博客 4403

何为动态规划

动态规划(英语:Dynamic programming,简称 DP),通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。简单来说,动态规划其实就是,给定一个问题,我们把它拆成一个个子问题,直到子问题可以直接解决。然后呢,把子问题答案保存起来,以减少重复计算。再根据子问题答案反推,得出原问题解的一种方法(类似于递推)。

wh233z的博客 1114

DCA决策曲线的解读和代码实现

0.决策曲线 决策曲线分析法(Decision Curve Analysis,DCA)是个与ROC曲线相提并论的相对比较新的模型评价方法。 关于它的原理,长篇大论的医学统计学知识解读实在不是我的强项,放上两个链接偷偷懒: https://www.plob.org/article/12455.html https://www.iikx.com/news/statistics/1622.html 如果想看文章,这篇里的fig7就是:https://www.ncbi.nlm.nih.gov/pmc/artic

weixin_42960896的博客 1万+

动态规划的工作原理,实现方式,应用场景

动态规划(Dynamic Programming,简称 DP)是一种在数学、计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式来求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。

一如既往的准备文章 1861

智能车‘大脑’的诞生:揭秘电磁循迹代码背后的控制论与系统思维

本文深入探讨智能车电磁循迹系统的核心架构与控制原理,揭示了其作为精密闭环系统如何通过模块化设计、信号处理、PID控制算法及电机驱动实现自主导航。重点分析了电磁信号采集与处理流程框架,为开发者提供从理论到实践的全面指南。

arduino9maker的博客 531

C#实战:如何从零构建一个轻量级软PLC梯形图编辑器(附完整源码)

本文详细介绍了如何使用C#从零开始构建一个轻量级软PLC梯形图编辑器。内容涵盖项目架构设计、GDI+图形渲染、编辑交互实现以及核心的扫描执行引擎开发,并提供了定时器、计数器等高级功能的实现思路与完整源码,为开发者深入工业自动化领域提供了实战指南。

4k5l6j7h8的博客 257

DP概念和编程方法

但是值得我们思考的是:在一些时候我们对一个同样的子问题会进行多次重复计算,耗费了大量的时间(参考斐波那契数列)。用DP处理重叠子问题时,每个子问题只计算一次,从而避免了重复计算,这就是DP效率高的原因。首先,子问题是原大问题的小版本,计算步骤一致;其次,计算大问题时,需要多次重复计算小问题。处理DP中的大问题和小问题。具体做法:首先分析得到最优子结构,然后递归或带记忆化搜索的递归进行编程,从而实现高效的计算。DP是求解多阶段决策问题最优化的一种算法思想,它用于解决具有重叠子问题、具有子结构特征的问题。

xbj319wz的博客 597

模拟IC版图DRC实战:手把手教你搞定MIM电容天线错误和ESD.10g违例

本文深入解析模拟IC版图中的MIM电容天线错误和ESD.10g违例问题,提供从错误解码到实战修复的完整指南。通过电路图修改、版图优化和验证闭环等步骤,帮助工程师高效解决DRC错误,特别适用于ADC等精密模拟电路设计。文章还分享了高级排错技巧和预防性设计策略,大幅提升流片成功率。

weixin_27697385的博客 295

profibus dp协议_深入理解PROBIFUS-DP协议的工作原理

前面的文章我们介绍了S7-200 Smart用于PROFIBUS-DP网络的模块-EM DP01,有读者反馈对PROFIBUS-DP协议的工作原理不清楚。今天这篇文章,我们就来深入介绍下PROFIBUS-DP协议。“PROFIBUS”是英文“Process Field Bus”的简写,中文翻译为“过程现场总线”。PROFIBUS协议是一种开放式、不依赖于生产商的国际标准总线协议,最早收录于欧洲标准...

weixin_39919165的博客 4123

GJB软件需求规格说明-编制指南

《软件需求规格说明》是软件开发过程必不可少的文档,并且需要评审,也是后续鉴定试验的依据,因此,非常有必要认真对待此文档。 文档对于很多开发技术人员来说是一件非常头疼的事,虽然标准有要求,但是光看标准还是难以下笔。 本文将详细讲解如何按照GJB 438C的要求编写《软件需求规格说明》。 注:文中有背景颜色内容为标准内容,加粗部分为范例。斜体部分为解释。 全文1.3万字

Abel的博客 6357

Cesium 场景特效之【全球海洋】GL2兼容版

Cesium 场景特效之【全球海洋】GL2兼容版

喜欢钻研前端和分享技术 1278

【缄*默】 #DP# 各种DP的实现方法(更新ing)

DP =「状态」+「阶段」+「决策」 基本原理 = 「有向无环图」+「最优子结构」+「无后效性」   目录 一. 线性DP { 1.概念引入 } { 2.例题详解 } 【例题1】caioj 1064 最长上升子序列 【例题2】caioj 1068 最长公共子序列 【例题3】洛谷 p1216 数字三角形 【例题4】poj 2279 Picture Permutations 【例...

flora715的博客 1671

【Block总结】DiffAttention,差分注意力融入CLIP架构,抑制噪声,增强对相关特征的关注|即插即用

DiffCLIP通过微小的结构改动实现了显著的性能提升,为多模态模型的轻量化与高效化提供了新范式。

AI浩 1561

【第五讲】 动态规划

动态规划 背包问题;线性DP;区间DP;计数类DP;数位统计DP;状态压缩DP;树形DP;记忆化搜索

Ws_zqw的博客 1296

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

理解区间dp和例题

一、讲解 1、作用 用于不知道从哪里合并的动态规划题,不比线性dp 2、解法步骤 即枚举区间长度,再枚举左端点,之后枚举区间的断点进行转移。 3、核心思路 既然让我求解在一个区间上的最优解,那么我把这个区间分割成一个个小区间, 求解每个小区间的最优解,再合并小区间得到大区间即可。所以在代码实现上, 我可以枚举区间长度len为每次分割成的小区间长度(由短到长不断合并), 内层枚举该长度下可以的起点,自然终点也就明了了。然后在这个起点终点之间枚举分割点, 求解这段小区间在某个分割点下的最优解。 4、图解法 二

qq_42757083的博客 212

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

树形dp 入门模板题

树形dp • 树型dp一般先算子树然后进行合并,在实现上与树的后序遍历(这个说法并不准确,因为其实很多都不是二叉树)类似——遍历子树,遍历完之后把子树的值合并给父亲。 • 大部分的树形dp都是利用递归,先确定叶子结点的特征,然后推上去,即状态转移方程一般都是有该点的某些特征 = 其子节点的一些特征 • 有的树形dp以哪个点为根节点无所谓,有的有所谓。 引入 • 给你一棵n个点的树(1号点为根节点),求以点i为根的子树的大小 • f[i]以点i为根的子树的点的个数 • f[i] = 1+Σf[k] (k是i的

qq_33969563的博客 1192

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

2.3 动态规划1

在一个圆形操场的四周摆放 NN 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

Blueqwq的小黑屋 315

【DP_区间DP专辑】

自从天津网赛后心憔悴啊,发现DP还是太水了,1006那道区间DP竟然不会做,咳,果然是赤裸裸的水军。     今天花了一天时间写了几道区间DP的题目,大部分都是水题,然后和以前的合并起来就是这个9道题的专辑,大家可以试着AK。      区间DP是一类在区间上进行动态规划的最优问题,一般是根据问题设出一个表示状态的dp,可以是二维的也可以是三维的,一般情况下为二维。然后将问题划分成两个子问题,

8441

动态规划-总结分析

❁声明:本文非完全原创性,其中参考资料摘自OI-WIKI:http://oi-wiki.com/dp/basic/,如有侵权,请联系我删除! 动态规划应用于子问题重叠的情况: 1. 要去刻画最优解的结构特征; 2. 尝试递归地定义最优解的值(就是我们常说的考虑从 $i - 1$ 转移到 $i$ ); 3. 计算最优解; 4. 利用计算出的信息构造一个最优解。 本文对动态规划进行详细的讨论和展开,可以作为学习、复习参考

HeartFireY的博客 931

【题解&刷题总结】HDU 3507(斜率优化dp)

最近学了学斜率dp,刷了几道题,感觉挺不错。 题目 Zero has an old printer that doesn’t work well sometimes. As it is antique, he still like to use it to print articles. But it is too old to work for a long time and it will certainly wear and tear, so Zero use a cost to evaluate t

LeeCongWei的博客 368

动态规划之状态压缩dp入门

状态压缩动态规划(简称状压dp)是另一类非常典型的动态规划,通常使用在NP问题的小规模求解中,虽然是指数级别的复杂度,但速度比搜索快,其思想非常值得借鉴。 为了更好的理解状压dp,首先介绍位运算相关的知识。 1.’&’符号,x&y,会将两个十进制数在二进制下进行与运算,然后返回其十进制下的值。例如3(11)&2(10)=2(10)。 2.’|’符号,x|y,会将两个十进制数在二进制下进行或运

qxAi的博客 5万+

树形动态规划 java_树形动态规划

我颓了今天复习一下树形DP一道简单的入门树形DP代码如下#include#include#includeusing namespace std;const int maxn=10007;int dp[maxn][2];bool f[maxn][2];int v[maxn];int cnt[maxn];int son[maxn][maxn];int fa[maxn];int work(int a,i...

weixin_39890652的博客 187

Part 4.4 树形动态规划

树形动态规划,即在树上进行的动态规划。因为树的递归性质,树形动态规划一般都是递归求解的。

m0_70897036的博客 1158

动态规划专题(二)——树形DP

前言 DPDPDP这东西真的是博大精深啊… 简介 树形DPDPDP,顾名思义,就是在树上操作的DPDPDP,一般可以用fif_ifi​表示以编号为iii的节点为根的子树中的最优解。 转移的时候一般都将信息由子节点转移到父亲节点,也就是将信息从下往上转移。 因此,一般树形DPDPDP都会采用 递归 的形式。 几道例题 好吧,DPDPDP好像也没什么东西可讲,这样介绍得还是不够具体。干脆直接看例题...

CXR的博客 459

树形dp入门模板题-没有上司的舞会(动态规划)

题目描述 某大学有N个职员,编号为1~N。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。 输入格式 第一行一个整数N。(1<=N&...

qq_41022094的博客 597
上一篇: DPDK收发包流程分析(一)
下一篇: 《算法图解》之狄克斯特拉算法
废言Pro
博客等级 码龄16年 1093粉丝 142原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值