动态规划算法举例分析
- 格式:doc
- 大小:85.50 KB
- 文档页数:10
动态规划算法及其在序列比对中应用分析序列比对是生物信息学中一个重要的问题,用于比较两个或多个生物序列的相似性和差异性。
在序列比对过程中,动态规划算法是一种常用和有效的方法。
本文将介绍动态规划算法的基本原理和应用,并深入分析其在序列比对中的应用。
1. 动态规划算法基本原理动态规划算法是一种通过把问题分解为相互重叠的子问题,并通过将每个子问题的解存储起来来解决复杂问题的方法。
它通常用于处理具有重叠子问题和最优子结构特性的问题。
动态规划算法的核心思想是将原问题拆解成若干个子问题,通过计算每个子问题的最优解来得到原问题的最优解。
这个过程可以通过建立一个状态转移方程来实现,即找到子问题之间的关联关系。
2. 动态规划在序列比对中的应用序列比对是生物信息学研究中常见的任务之一,用于比较两个或多个生物序列的相似性和差异性。
动态规划算法在序列比对中被广泛应用,最为著名的例子是Smith-Waterman算法和Needleman-Wunsch算法。
2.1 Smith-Waterman算法Smith-Waterman算法是一种用于局部序列比对的动态规划算法。
它通过为每个可能的比对位置定义一个得分矩阵,并计算出从每个比对位置开始的最优比对路径来找到最优的局部比对。
Smith-Waterman算法的基本思路是从比对矩阵的右下角开始,根据得分矩阵中每个位置的得分值和其周围位置的得分值进行计算,并记录下最大得分值及其对应的路径。
最终,通过回溯从最大得分值开始的路径,得到最优的局部比对结果。
2.2 Needleman-Wunsch算法Needleman-Wunsch算法是一种用于全局序列比对的动态规划算法。
它通过为每个比对位置定义一个得分矩阵,并通过计算出从第一个比对位置到最后一个比对位置的最优比对路径来找到最优的全局比对。
Needleman-Wunsch算法的基本思路与Smith-Waterman算法类似,但不同之处在于需要考虑序列的开头和结尾对比对结果的影响。
动态规划算法及其应用案例解析动态规划算法是计算机科学中一种非常重要的算法,它在许多领域都有大量的应用。
在本文中,我们将介绍动态规划算法的基本思想和特点,并通过一些常见的应用案例来深入理解这个算法。
1. 动态规划算法的基本思想动态规划算法是一种算法设计技术,用于在多阶段决策过程中寻找最优解。
它的基本思想是将一个大问题分解成较小的子问题来解决,然后将这些子问题的解组合起来得到原问题的解。
它与分治算法很类似,但是动态规划算法通常是针对问题的重复性结构进行优化的。
动态规划算法通常适用于满足以下几个条件的问题:(1)问题具有重叠子问题的特点,即一个大问题可以分解为多个子问题,且这些子问题存在相同的子结构;(2)问题具有最优子结构的特点,即一个问题的最优解包含其子问题的最优解。
通过以上两个条件,在通过子问题的最优解推导出大问题的最优解时,我们可以避免重复计算并且保证得到的结果是最优的。
2. 动态规划算法的特点动态规划算法的主要特点包括以下几个方面:(1)动态规划算法使用一个递推公式来计算问题的解,这个递推公式通常是由原问题和子问题之间的关系建立而来的。
(2)动态规划算法使用一个表格来存储子问题的解,这个表格通常称为动态规划表或者状态转移表。
(3)动态规划算法通常需要进行一些预处理操作,例如初始化表格的值,以及确定递推公式的边界条件。
(4)动态规划算法的时间复杂度通常是由子问题的个数和计算每个子问题的时间复杂度来决定的。
3. 应用案例解析下面我们将通过一些常见的应用案例来更好地理解动态规划算法。
(1)背包问题背包问题是指给定一组物品和一个容量为W的背包,选择一些物品放入背包中,使得放入背包的物品的总价值最大。
这个问题可以通过动态规划算法来解决。
我们可以定义一个二维数组f[i][j],表示前i个物品放进容量为j的背包所得到的最大价值。
递推公式可以定义为:f[i][j] = max(f[i-1][j], f[i-1][j-w[i]] + v[i]),其中w[i]表示第i个物品的重量,v[i]表示第i个物品的价值。
动态规划算法的详细原理及使用案例一、引言动态规划是一种求解最优化问题的算法,它具有广泛的应用领域,如机器学习、图像处理、自然语言处理等。
本文将详细介绍动态规划算法的原理,并提供一些使用案例,以帮助读者理解和应用这一算法的具体过程。
二、动态规划的基本原理动态规划算法通过将问题分解为多个子问题,并利用已解决子问题的解来求解更大规模的问题。
其核心思想是利用存储技术来避免重复计算,从而大大提高计算效率。
具体来说,动态规划算法通常包含以下步骤:1. 定义子问题:将原问题分解为若干个子问题,这些子问题具有相同的结构,但规模更小。
这种分解可以通过递归的方式进行。
2. 定义状态:确定每个子问题的独立变量,即问题的状态。
状态具有明确的定义和可计算的表达式。
3. 确定状态转移方程:根据子问题之间的关系,建立状态之间的转移方程。
这个方程可以是简单的递推关系式、递归方程或其他形式的方程。
4. 解决问题:使用递推或其他方法,根据状态转移方程求解每个子问题,直到获得最终解。
三、动态规划的使用案例1. 背包问题背包问题是动态规划算法的经典案例之一。
假设有一个背包,它能容纳一定重量的物品,每个物品有对应的价值。
目的是在不超过背包总重量的前提下,选取最有价值的物品装入背包。
这个问题可以通过动态规划算法来求解。
具体步骤如下:(1)定义问题:在不超过背包容量的限制下,选取物品使得总价值最大化。
(2)定义状态:令dp[i][j]表示将前i个物品放入容量为j的背包中所能获得的最大价值。
(3)状态转移方程:dp[i][j] = max(dp[i-1][j-w[i]]+v[i], dp[i-1][j]),其中w[i]为第i个物品的重量,v[i]为第i个物品的价值。
(4)解决问题:根据状态转移方程依次计算每个子问题的解,并记录最优解,直到获得最终答案。
2. 最长公共子序列最长公共子序列(Longest Common Subsequence,简称LCS)是一种经典的动态规划问题,它用于确定两个字符串中最长的共同子序列。
动态规划算法详解及经典例题⼀、基本概念(1)⼀种使⽤多阶段决策过程最优的通⽤⽅法。
(2)动态规划过程是:每次决策依赖于当前状态,⼜随即引起状态的转移。
⼀个决策序列就是在变化的状态中产⽣出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。
假设问题是由交叠的⼦问题所构成,我们就能够⽤动态规划技术来解决它。
⼀般来说,这种⼦问题出⾃对给定问题求解的递推关系中,这个递推关系包括了同样问题的更⼩⼦问题的解。
动态规划法建议,与其对交叠⼦问题⼀次重新的求解,不如把每⼀个较⼩⼦问题仅仅求解⼀次并把结果记录在表中(动态规划也是空间换时间的)。
这样就能够从表中得到原始问题的解。
(3)动态规划经常常使⽤于解决最优化问题,这些问题多表现为多阶段决策。
关于多阶段决策:在实际中,⼈们经常遇到这样⼀类决策问题,即因为过程的特殊性,能够将决策的全过程根据时间或空间划分若⼲个联系的阶段。
⽽在各阶段中。
⼈们都须要作出⽅案的选择。
我们称之为决策。
⽽且当⼀个阶段的决策之后,经常影响到下⼀个阶段的决策,从⽽影响整个过程的活动。
这样,各个阶段所确定的决策就构成⼀个决策序列,常称之为策略。
因为各个阶段可供选择的决策往往不⽌⼀个。
因⽽就可能有很多决策以供选择,这些可供选择的策略构成⼀个集合,我们称之为同意策略集合(简称策略集合)。
每⼀个策略都对应地确定⼀种活动的效果。
我们假定这个效果能够⽤数量来衡量。
因为不同的策略经常导致不同的效果,因此,怎样在同意策略集合中选择⼀个策略,使其在预定的标准下达到最好的效果。
经常是⼈们所关⼼的问题。
我们称这种策略为最优策略,这类问题就称为多阶段决策问题。
(4)多阶段决策问题举例:机器负荷分配问题某种机器能够在⾼低两种不同的负荷下进⾏⽣产。
在⾼负荷下⽣产时。
产品的年产量g和投⼊⽣产的机器数量x的关系为g=g(x),这时的年完善率为a,即假设年初完善机器数为x,到年终时完善的机器数为a*x(0<a<1);在低负荷下⽣产时,产品的年产量h和投⼊⽣产的机器数量y 的关系为h=h(y)。
动态规划例题动态规划是一种以最优化原理为基础的问题求解方法,通过拆分问题为若干阶段,每个阶段求解一个子问题,再逐步推导出整个问题的最优解。
例如,有一个背包能够承受一定的重量,现有一些物品,每个物品都有自己的重量和价值。
我们希望将物品放入背包中,使得背包的总价值最大。
这个问题可以用动态规划来解决。
首先,我们定义一个二维数组dp,其中dp[i][j]表示在前i个物品中,容量为j的背包中所能放入的物品的最大价值。
那么,对于每一个物品,可以选择放入背包或者不放入背包。
如果选择放入背包,最大价值为dp[i-1][j-w[i]] + v[i],其中w[i]表示第i个物品的重量,v[i]表示第i个物品的价值。
如果选择不放入背包,最大价值为dp[i-1][j]。
因此,dp[i][j]的状态转移方程为:dp[i][j] = max(dp[i-1][j-w[i]] + v[i], dp[i-1][j])。
基于这个状态转移方程,可以逐步求解从第1个物品到第n个物品的最大价值。
最终,dp[n][W]即为问题的最优解,其中W 表示背包的容量。
举个简单的例子,假设背包的容量为10,有3个物品,它们的重量分别为3、4、5,价值分别为4、5、6。
此时,可以得到如下的dp矩阵:0 0 0 0 0 0 0 0 0 0 00 0 0 4 4 4 4 4 4 4 40 0 0 4 5 5 9 9 9 9 90 0 0 4 5 5 9 10 10 14 14我们可以看到,dp[3][10]的最大价值为14,表示在前3个物品中,容量为10的背包中所能放入的物品的最大价值为14。
通过动态规划,我们可以有效地求解背包问题,得到物品放入背包的最优解。
这个例子只是动态规划的一个简单应用,实际上,动态规划可以解决各种复杂的问题,如最长公共子序列、最大子数组和、最大字段和等。
因此,学习动态规划是非常有意义的。
动态规划算法的常见实例动态规划算法是一种将复杂问题分解为简单子问题来解决的算法,它可被应用于多个领域中,如经济学、生物学、计算机科学等。
在本文中,我们将详细讨论动态规划算法的常见实例。
一、最长公共子序列问题最长公共子序列(LCS)问题是一个经典的计算机科学问题,它要求在两个字符串中找到最长的相同连续子序列。
例如,对于字符串“ABCD”和“ACDF”,最长公共子序列为“ACD”。
使用动态规划方法来解决LCS问题。
首先定义一个m行n列的二维矩阵,其中m和n分别表示两个字符串的长度。
然后,使用以下递推关系:1. 如果一个字符串的长度为0,LCS为0。
2. 如果两个字符不相同,则LCS为它们的前一个字符集合和它们的后一个字符集合的最大值。
3. 如果两个字符相同,则LCS为它们的前一个字符集合和它们的后一个字符集合所组成的子序列中的最大值加1。
最后,矩阵右下角的值就是LCS的长度。
二、背包问题背包问题(Knapsack problem)是一个经典的组合优化问题,被广泛应用于计算机科学和其他领域。
在一个决策者必须决定是否将某些物品放入背包中的场景中,背包问题就发挥了作用。
具体来说,我们要解决的问题是:对于一个固定容量的背包,有一些物品,它们的重量和价值都不同,如何在不超过背包容量的前提下,使所装载物品的总价值最大化。
一种解决方案是使用动态规划方法。
定义一个二维数组,其行表示物品,列表示背包大小。
然后,使用以下递推关系:1. 如果所考虑的物品重量大于背包容量,则不选此物品。
2. 否则,在选取该物品和不选该物品两种情况中选择最优解作为最终结果。
最后,矩阵中右下角的值就是最大的总价值。
三、矩阵链乘法矩阵链乘法是一种计算矩阵乘积的优化算法。
它使用动态规划算法来确定矩阵乘积的最小值。
对于一个长度为n的矩阵链,我们可以定义一个n×n 的矩阵M,其中第i行第j列的元素Mi,j表示第i个矩阵与第j个矩阵相乘的最小次数。
信息学奥赛——树型动态规划的实例分析树型动态规划(Tree Dynamic Programming)是信息学奥赛中常用的一种算法思想,在解决一些与树相关的问题时非常有效。
本文将通过一个具体的实例对树型动态规划进行详细分析。
假设有一棵有根树,每个节点上都有一个非负整数权值,并且每个节点下都可能有若干个子节点。
现在要求选择一些节点,使得选中的节点的权值之和尽可能大,但是不能选择相邻的节点。
我们需要设计一个算法来解决这个问题。
首先,我们可以观察到如果一个节点被选中,那么它的子节点就不能被选中。
于是,我们可以定义一个动态规划的状态dp[i]表示以节点i为根的子树中选择节点的最大权值之和。
对于根节点,我们有两种情况:1. 根节点i被选择,那么它的子节点就不能被选择。
所以dp[i] = sum(dp[j]),其中j表示i的所有子节点。
2. 根节点i不被选择,那么它的所有子节点都可以被选择。
所以dp[i] = sum(max(dp[j], dp[k])),其中j和k分别表示i的所有孩子节点的子节点。
通过对根节点的两种状态的分析,我们可以得到一个递推关系:dp[i] = max(sum(dp[j]), sum(max(dp[k], dp[l]))),其中j表示i的所有子节点,k和l分别表示i的所有孩子节点的子节点。
接下来,我们需要设计一个合适的递归算法来计算dp[i]。
我们可以使用深度优先(DFS)的方式来处理每个节点,实现递归的过程。
具体的伪代码如下:```DFS(i):visit[i] = truefor j in i的所有子节点:if visit[j] == false:DFS(j)dp[i] += dp[j]for k in i的所有孩子节点:for l in k的所有子节点:dp[i] += max(dp[k], dp[l])```最后,我们只需要调用DFS函数以根节点为参数,可以得到整棵树的最优解。
动态规划算法设计方法及案例解析动态规划是一种解决多阶段决策问题的常用算法,通过将问题分解为多个子问题,并通过求解子问题的最优解来得到原问题的最优解。
本文将介绍动态规划算法的设计方法,并通过两个实例进行解析,以帮助读者更好地理解和应用该算法。
一、动态规划算法设计方法动态规划算法的设计一般遵循以下几个步骤:1. 确定问题的状态:将原问题划分为若干个子问题,并定义每个子问题的状态。
状态的定义应该包含子问题的变量和可以从子问题中获得的信息。
2. 定义状态转移方程:通过分析子问题之间的关系,确定状态之间的转移方式。
通常使用递推关系式来描述状态之间的转移,以表达每个子问题的最优解与其他子问题解之间的关系。
3. 确定初始状态和边界条件:确定问题的初始状态和边界条件,即最简单的子问题的解,作为求解其他子问题的基础。
4. 计算最优解:根据定义的状态转移方程,利用递推的方式从初始状态开始逐步计算每个子问题的最优解,直到得到原问题的最优解。
二、案例解析1:背包问题背包问题是动态规划算法中经典的案例之一,主要解决如何在限定容量的背包中选择一些物品,使得物品的总价值最大。
以下是一个简化的例子:假设有一个容量为C的背包,以及n个物品,每个物品有重量wi 和价值vi。
要求选择一些物品放入背包中,使得放入背包中物品的总价值最大。
根据动态规划算法的设计方法,我们可以定义子问题的状态为:背包容量为c,前a个物品的最优解用F(c,a)表示。
那么,状态转移方程可以定义为:F(c,a) = max{F(c,a-1), F(c-wa, a-1) + va}其中,F(c,a-1)表示不选择第a个物品时的最优解,F(c-wa, a-1) + va 表示选择第a个物品时的最优解。
初始状态为F(0,a) = F(c,0) = 0,边界条件为c < wa时,F(c,a) =F(c,a-1)。
根据以上定义,我们可以通过递推的方式计算F(c,n),从而得到背包问题的最优解。
动态规划应用案例动态规划是一种解决复杂问题的优化算法。
它通过将问题拆分成多个子问题,并记录每个子问题的解,以避免重复计算,从而提高算法的效率。
在实际应用中,动态规划被广泛用于解决各种问题,包括最优化问题、路径搜索问题、序列问题等。
本文将介绍几个动态规划的应用案例,以展示其在实际问题中的强大能力。
案例一:背包问题背包问题是动态规划中经典的一个例子。
假设有一个背包,容量为V,现有n个物品,每个物品的重量为wi,价值为vi。
要求在不超过背包容量的前提下,选取一些物品放入背包,使得背包中的物品总价值最大。
这个问题可以用动态规划来解决。
首先定义一个二维数组dp,其中dp[i][j]表示在前i个物品中选择一些物品,使得它们的总重量不超过j时的最大总价值。
然后,可以得到如下的状态转移方程:dp[i][j] = max(dp[i-1][j], dp[i-1][j-wi] + vi)最后,根据状态转移方程,可以循环计算出dp[n][V]的值,即背包中物品总价值的最大值,从而解决了背包问题。
案例二:最长递增子序列最长递增子序列是指在一个序列中,选取一些数字,使得这些数字按照顺序排列,且长度最长。
动态规划也可以应用于解决最长递增子序列问题。
假设有一个序列nums,长度为n。
定义一个一维数组dp,其中dp[i]表示以nums[i]为结尾的最长递增子序列的长度。
然后,可以得到如下的状态转移方程:dp[i] = max(dp[j] + 1),其中j < i且nums[j] < nums[i]最后,循环计算出dp数组中的最大值,即为最长递增子序列的长度。
案例三:最大子数组和最大子数组和问题是指在一个数组中,选取一段连续的子数组,使得子数组的和最大。
动态规划也可以用于解决最大子数组和问题。
假设有一个数组nums,长度为n。
定义一个一维数组dp,其中dp[i]表示以nums[i]为结尾的连续子数组的最大和。
然后,可以得到如下的状态转移方程:dp[i] = max(dp[i-1] + nums[i], nums[i])最后,循环计算出dp数组中的最大值,即为最大子数组的和。
动态规划问题常见解法动态规划(Dynamic Programming)是一种常用的算法思想,用于解决一类具有重叠子问题性质和最优子结构性质的问题。
动态规划通常通过将问题划分为若干个子问题,并分别求解子问题的最优解,从而得到原问题的最优解。
以下是动态规划问题常见的解法:1. 斐波那契数列斐波那契数列是动态规划问题中的经典案例。
它的递推关系式为 F(n) = F(n-1) + F(n-2),其中 F(0) = 0,F(1) = 1。
可以使用动态规划的思想来解决斐波那契数列问题,通过保存已经计算过的子问题的结果,避免重复计算。
2. 背包问题背包问题是一个经典的优化问题,可以使用动态规划的方法进行求解。
背包问题包括 0/1 背包问题和完全背包问题。
0/1 背包问题中每个物品要么被选中放入背包,要么不选。
完全背包问题中每个物品可以被选中多次放入背包。
通过定义状态转移方程和使用动态规划的思想,可以高效地求解背包问题。
3. 最长递增子序列最长递增子序列是一个常见的子序列问题,可以使用动态规划的方法进行求解。
最长递增子序列指的是在一个序列中,找到一个最长的子序列,使得子序列中的元素按照顺序递增。
通过定义状态转移方程和使用动态规划的思想,可以有效地求解最长递增子序列问题。
4. 最长公共子序列最长公共子序列是一个经典的字符串问题,可以使用动态规划的方法进行求解。
给定两个字符串,找到它们之间最长的公共子序列。
通过定义状态转移方程和使用动态规划的思想,可以高效地求解最长公共子序列问题。
5. 矩阵链乘法矩阵链乘法是一个求解最优括号化问题的经典案例,可以使用动态规划的方法进行求解。
给定多个矩阵的大小,需要找到一个最优的计算顺序,使得计算乘积的次数最少。
通过定义状态转移方程和使用动态规划的思想,可以高效地求解矩阵链乘法问题。
以上是动态规划问题的常见解法,通过使用动态规划的思想和方法,可以解决这些问题,并求得最优解。
Matlab中的动态规划方法与示例分析引言动态规划是一种解决多阶段决策问题的优化方法,它通过将问题分解为若干阶段,在每个阶段中做出最优决策,从而得到整体最优解。
Matlab作为一种强大的计算工具,提供了丰富的函数和工具箱来支持动态规划的求解。
本文将通过介绍动态规划的基本原理和算法,结合几个实际示例,展示在Matlab中如何应用动态规划方法解决实际问题。
一、动态规划的基本原理动态规划的基本原理是通过自底向上的递推关系,将一个大问题分解为若干个子问题,并将每个子问题的最优解存储起来,以便在解决更大的问题时进行查找和利用。
具体地,动态规划有三个关键要素:最优子结构、边界条件和状态转移方程。
最优子结构是指一个问题的最优解可以由其子问题的最优解组成。
它是动态规划的关键特点,也是将问题分解为子问题并递归求解的基础。
边界条件是指问题的边界情况和初始状态,可以是递归求解的终止条件。
状态转移方程是指描述子问题之间关系的方程,它将子问题的最优解与大问题的最优解联系起来。
在求解过程中,通过将问题划分为子问题并依次求解,最终得到整体最优解。
二、动态规划的算法实现在Matlab中,可以通过定义递归函数或使用循环结构来实现动态规划算法。
递归函数的实现方式简单直观,但由于递归调用的开销较大,可能导致算法的效率较低。
循环结构的实现方式相对复杂,但可以通过数组或矩阵来存储子问题的最优解,以减少重复计算,提高算法的效率。
在实际应用中,动态规划可以通过以下步骤来实现:1. 确定问题的最优子结构、边界条件和状态转移方程。
2. 定义数组或矩阵来存储子问题的最优解。
3. 利用循环结构或递归函数,按照自底向上的顺序计算和存储子问题的最优解。
4. 根据存储的子问题最优解,计算并返回大问题的最优解。
三、动态规划实例分析1. 背包问题背包问题是动态规划中经典的例子,它的目标是在限制总重量的情况下,选择一些物品放入背包,使得背包中物品的总价值最大化。
动态规划是一种用于解决最优化问题的算法。
它通常用于找到最小或最大值。
这里列举了12 个常见的动态规划算法,并给出了每个算法的举例:
1 最长公共子序列(LCS)算法:用于比较两个序列,找出它们之
间的最长公共子序列。
2 最小编辑距离算法:用于比较两个字符串,找出将一个字符串变
为另一个字符串所需的最少编辑操作次数。
3 背包问题算法:用于在限制给定的总体积的情况下选择最优的物
品组合。
4 最短路径算法:用于求解有向图或路径的最短路径。
5 最小生成树算法:用于求解图的最小生成树。
6 线性规划算法:用于求解线性规划问题。
7 矩阵链乘法算法:用于计算矩阵链乘法的最优计算次序。
8 单源最短路径算法:用于求解有向图的单源最短路径问题。
9 拓扑排序算法:用于对有向无环图(DAG)进行拓扑排序。
10图形相似性算法:用两个图形进行对齐,并通过比较它们之间的差异来评估它们的相似程度。
11 11 区间动态规划算法:用于解决区间动态规划问题,例如
最小编辑代价问题。
12 分数背包问题算法:用于在限制给定的总价值的情况下选择
最优的物品组合。
13这些算法的具体细节及实现方式可以通过搜索或者学习相
关的资料来了解。
动态规划算法1. 动态规划算法介绍基本思想是将待求解问题分解成若干子问题,先求解子问题,最后用这些子问题带到原问题,与分治算法的不同是,经分解得到的子问题往往是不是相互独立,若用分治则子问题太多。
2. 适用动态规划算法问题的特征(1)最优子结构设计动态规划算法的第一步骤通常是要刻画最优解的结构。
当问题的最优解包含了其子问题的最优解时,称该问题具有最优子结构性质。
问题的最优子结构性质提供了该问题可用动态规划算法求解的重要线索。
在动态规划算法中,问题的最优子结构性质使我们能够以自底向下的方式递归地从子问题的最优解逐步构造出整个问题的最优解。
同时,它也使我们能在相对小的子问题空间中考虑问题。
(2)重叠子问题可用动态规划算法求解的问题应具备的另一基本要素是子问题的重叠性质。
在用递归算法自顶向下解此问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。
动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只解一次,而后将其解保存在一个表格中,当再次需要解此子问题时,只有简单地用常数时间查看一下结果。
通常,不同的子问题个数随输入问题的大小呈多项式增长。
因此,用动态规划算法通常只需要多项式时间,从而获得较高的解题效率。
(3)备忘录方法动态规划算法的一个变形是备忘录方法。
备忘录方法也是一个表格来保存已解决的子问题的答案,在下次需要解此子问题时,只要简单地查看该子问题的解答,而不必重新计算。
与动态规划算法不同的是,备忘录方法的递归方式是自顶向下的,而动态规划算法则是自底向上递归的。
因此,备忘录方法的控制结构与直接递归方法的控制结构相同,区别在于备忘录方法为每个解过的子问题建立了备忘录以备需要时查看,避免了相同子问题的重复求解。
备忘录方法为每个子问题建立一个记录项,初始化时,该记录项存入一个特殊的值,表示该子问题尚未求解。
在求解过程中,对每个待求的子问题,首先查看其相应的记录项。
若记录项中存储的是初始化时存入的特殊值,则表示该子问题是第一次遇到,则此时计算出该子问题的解,并保存在其相应的记录项中。
若记录项中存储的已不是初始化时存入的特殊值,则表示该子问题已被计算过,其相应的记录项中存储的是该子问题的解答。
此时,只要从记录项中取出该子问题的解答即可。
3. 基本步骤a 、找出最优解的性质,并刻画其结构特征。
b 、递归地定义最优值。
c 、以自底向上的方式计算出最优值。
d 、根据计算最优值时得到的信息构造一个最优解。
(可省) 例1-1 [0/1背包问题] [问题描述]用贪心算法不能保证求出最优解。
在0/1背包问题中,需要对容量为c 的背包进行装载。
从n 个物品中选取装入背包的物品,每件物品i 的重量为iw ,价值为iv 。
对于可行的背包装载,背包中物品的总重量不能超过背包的容量,最佳装载是指所装入的物品价值最高,即∑=ni iix v1取得最大值。
约束条件为cx wni i i≤∑=1,{}()n i x i ≤≤∈11,0。
在这个表达式中,需要求i x的值。
i x=1表示物品i装入背包中,i x=0表示物品i不装入背包。
1.找出最优解性质考察上述0 / 1背包问题。
如前所述,在该问题中需要决定x1,…,xn的值。
假设按i = 1,2,…,n 的次序来确定xi 的值。
如果置x1 = 0,则问题转变为相对于其余物品(即物品2,3,…,n),背包容量仍为c 的背包问题。
若置x1 = 1,问题就变为关于最大背包容量为c-w1 的问题。
现设r∈{c,c-w1 } 为剩余的背包容量。
在第一次决策之后,剩下的问题便是考虑背包容量为r 时的决策。
不管x1 是0或是1,[x2 ,…,xn ] 必须是第一次决策之后的一个最优方案,如果不是,则会有一个更好的方案[y2,…,yn ],因而[x1,y2,…,yn ]是一个更好的方案。
因此最优解符合条件,cxwywxwniiiniii≤+≤∑∑==2111。
例:假设n=3, w=[100,14,10], p=[20,18,15], c= 116。
若设x1 = 1,则在本次决策之后,可用的背包容量为r= 116-100=16 。
[x2,x3 ]=[0,1] 符合容量限制的条件,所得值为1 5,但因为[x2,x3 ]= [1,0] 同样符合容量条件且所得值为1 8,因此[x2,x3 ] = [ 0,1] 并非最优策略。
即x= [ 1,0,1] 可改进为x= [ 1,1,0 ]。
若设x1 = 0,则对于剩下的两种物品而言,容量限制条件为11 6。
总之,如果子问题的结果[x2,x3 ]不是剩余情况下的一个最优解,则[x1,x2,x3 ]也不会是总体的最优解。
2.递归定义最优解在上述例题的0 / 1背包问题中,最优决策序列由最优决策子序列组成。
假设m(i,v) 表示例子中剩余容量为j,剩余物品为i,i+1,…,n 时的最优解的值,即:nn nw j w j v j n m ≤≤≥⎩⎨⎧=00),( (1)和{}ii i i w j w j j i m v w j i m j i m j i m ≤≥⎩⎨⎧++-++=0),1(),1(),,1(max ),((2)因此,m (1,c )是初始时最优问题的最优解。
现在计算xi 值,步骤如下:若m(1 ,c) =m( 2 ,c),则x1 = 0,否则x1 = 1。
接下来需从剩余容量c-w1中寻求最优解,用m(2, c-w1) 表示最优解。
依此类推,可得到所有的xi (i= 1,2,…,n) 值。
[0/1背包问题的C 语言实现算法]#define N 12void Knapsack(int v[],int w[],int c,int n,int m[6][N]) {int i,j,jMax,k;jMax=(w[n]-1<c?w[n]-1:c); for(i=0;i<=jMax;i++) m[n][i]=0;for(i=w[n];i<=c;i++) m[n][i]=v[n];for(i=n-1;i>1;i--){jMax=(w[i]-1<c?w[i]-1:c); for(j=0;j<=jMax;j++) m[i][j]=m[i+1][j]; for(j=w[i];j<=c;j++){k=j-w[i];if(m[i+1][j]<m[i+1][k]+v[i]) m[i][j]=m[i+1][k]+v[i]; elsem[i][j]=m[i+1][j]; } }m[1][c]=m[2][c]; if(c>=w[1]) {k=c-w[1];m[1][c]=(m[2][c]>m[2][k]+v[1])?m[2][c]:m[2][k]+v[1]; }}void Traceback(int m[6][N],int w[],int c,int n,int x[]){int i;for(i=1;i<N;i++){if(m[i][c]==m[i+1][c])x[i]=0;else{x[i]=1;c-=w[i];}}x[n]=(m[n][c])?1:0;}main(){int i,c=10,n=5,w[]={0,2,2,6,5,4},v[]={0,6,3,5,4,6}; int m[6][N]={0};int x[6]={0};int j;Knapsack(v,w,c,n,m);for(i=1;i<=n;i++){for(j=1;j<=c;j++)printf("%3d",m[i][j]);printf("\n");}Traceback(m,w,c,n,x);for(i=1;i<=n;i++){if(x[i])printf("%4d:%4d",i,v[i]);}printf("\n");}例2-1 [最短路径问题][问题描述]现有一张地图,各结点代表城市,两结点间连线代表道路,线上数字表示城市间的距离。
如图2所示,试找出从结点1到结点7的最短路径。
① ② ⑤③ ④ ⑦⑥图2 有向图1. 找出最优解性质在第一次决策到达了某个节点i v之后,不管iv 怎样确定,剩下的问题就是选择从iv 到jv 的最短路径。
在所有点对最短路径问题( a l l - p a i r sshorest-paths problem )中,要寻找有向图G 中每对顶点之间的最短路径。
也就是说,对于每对顶点(i, j),需要寻找从i 到j 的最短路径及从j 到i 的最短路径。
因此对于一个n 个顶点的图来说,需寻找p =n(n-1) 条最短路径。
假定图G 中不含有长度为负数的环路,只有在这种假设下才可保证G 中每对顶点(i, j) 之间总有一条不含环路的最短路径。
2. 递归定义最优值上述问题已经转化为寻找从i 到j 的最短路径。
若记最短路径的长度为ee[j].逊县将其初始值赋值为 。
设k 为从i 到j 中节点编号中最大的数,则用C (i ,j ,k )表示经过节点k 的<i ,j>通路的路径长度。
若C (i ,j ,k )< ee[j],则ee[j] =C (i ,j ,k )。
69 9454 5865 78如此寻遍<i,j>中每条通路,则得到的ee[j]为<i,j>的最短路径长度。
最后递归得到从节点1到节点7的最短路径长度。
[最短路径问题的C语言算法实现]#include <stdio.h>#define N 7 /* 顶点数目 */ #define I 999 /* 表示无穷大 */int graph[N][N] = { /* 图的邻接矩阵 */ {I, 4, 5, 8, I, I, I},{I, I, I, 6,6, I, I},{I, I, I, 5, I, 7, I},{I, I, I, I, 8, 9, 9},{I, I, I, I, I, I, 5},{I, I, I, I, I, I, 4},{I, I, I, I, I, I, I}};int List[N]; /* 存放拓扑序列 */ int TopologicalOrder(); /* 拓扑排序函数 */void main() /* 主函数 */ { int i, j, k, l;int ee[N]; /* 最短距离 */int path_e[N][N], n_e[N]; /* 记录路径数据 *//* 初始化数据 */for (i = 0; i < N; i++) {n_e[i] = 0; /* 到 i 的最短路线的结点数 */ee[i] = I; }ee[0] = 0; /* 初始化头结点 */path_e[0][0] = 0;n_e[0] = 1;/* 拓扑排序 */if (!TopologicalOrder())return;/* 对于拓扑序列,运用动态规划步步算出最短路线 */for (i = 0; i < N; i++) {k = List[i]; /* 提取拓扑序列的元素 */ for (j = 0; j < N; j++) /* 更新它所指向顶点的所有数据 */{ if (graph[k][j] != I) /* 寻找指向的顶点 */if (graph[k][j] + ee[k] < ee[j]) /* 如果新路径更短 */{ee[j] = graph[k][j] + ee[k]; /* 更新最短路径长度 */ for (l = 0; l < n_e[k]; l++) /* 更新最短路线 */ { path_e[j][l] = path_e[k][l];}path_e[j][l] = j;n_e[j] = l + 1;}}}}/* 输出结果到屏幕 */for (i = 0; i < N; i++) {printf("shortest(%d): %2d Path: ", i + 1, ee[i]);for (j = 0; j < n_e[i]; j++) {printf("%d ", path_e[i][j] + 1);}printf("\n");}}int TopologicalOrder(){int i, j, top, count;int indegree[N], Stack[N];top = 0; /* 栈顶标志 */for (i = 0; i < N; i++) {indegree[i] = 0; /* 初始化入度 */for (j = 0; j < N; j++) {if (graph[j][i] != I) { /* 如连通 */indegree[i]++; /* 入度自增1 */}}if (!indegree[i]){ /* 如入度为零 */Stack[top++] = i; /* 入栈 */ }}count = 0;while (top != 0) /* 输出顶点数 */{ i = Stack[--top];List[count++] = i;for (j = 0; j < N; j++) {if (graph[i][j] != I) { /* 如连通 */if (!(--indegree[j])) { /* 而且入度为零 */Stack[top++] = j; /* 入栈 */}}}/* for */}/* while */return (count < N) ? 0 : 1;}例3 [航费问题]某航线价格表为:从亚特兰大到纽约或芝加哥,或从洛杉矶到亚特兰大的费用为$ 100;从芝加哥到纽约票价$20;而对于路经亚特兰大的旅客,从亚特兰大到芝加哥的费用仅为$20。