第三章 动态规划
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
怎样填?—— 分析: 1、表的维数 2、填表范围 3、按什么顺序填
1 1 2 3 4 5 6
0
2
0
3
2625
4
5
6
1 2 3 4 5 6
15750 7875 0
9375 11875 15125 4375 6000 7125 10500
750 0 2500 1000 5375 6250 3500 5000 0
• 重叠子问题性质
计算最优值方法2:递归+备忘录
备忘录方法采用一些表格来保存已解子问题的 信息(最优值、相应最优解的线索等)。每个子问 题初始化时都标记为尚未求解。在递归求解过程中, 对每个待解子问题,先查看它是否已求解。若未求 解,则计算其解并填表保存。若已求解,则查表取 出相应的结果。 书中把自上而下+备忘录的方法称为备忘录算法。
动态规划法求解步骤1——分析最优解的结构
思考:将矩阵连乘积AiAi+1…Aj记为A[i: j]。 设 A[1: n] 的其中一个最优解在矩阵 Ak 和 Ak+1 处断 开,即 A[1:n] = ( A[1:k] )×( A[k+1:n] )。
若 矩阵连乘问题具备最优子结构性质,则 : A[1: k] 和 A[k+1: n] 在 A[1:n]的最优解中的加 括号方式也分别是子问题 A1A2…AK 和 Ak+1Ak+2…An 的其中一个最优解。
2: 2
3: 3
3: 4
4: 4
2: 3
2: 2
4: 4
3: 3
1:来自百度文库2 1: 1
3: 3 2: 2
动态规划法求解步骤3——计算出最优值
计算最优值的两个方法: 1、自底向上 的 填表 方式 2、自顶向下 的 递归+备忘录 方式 该方式因为要避免重复计算同一子问题,所 以要引入备忘录。
计算最优值方法1:自底向上的填表方法
3.3 最长公共子序列(LCS)
若 给 定 序 列 X={x1,x2,…,xm} , 则 另 一 序 列 Z={z1,z2,…,zk} ,是 X 的子序列是指存在一个严格递
增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:
zj=xij。例如,序列Z={B,C,D,B}是序列X={A,B,C, B , D , A ,B} 的子序列,相应的递增下标序列为 {2 , 3 , 5,7}。 给定 2 个序列 X 和 Y ,当另一序列 Z 既是 X 的子序列又 是Y的子序列时,称Z是序列X和Y的公共子序列。 给 定 2 个 序 列 X={x1,x2,…,xm} 和 Y={y1,y2,…,yn} , 找出X和Y的最长公共子序列。
m[i][j]
0
1 2 3 4 5 6
1
1
2
3 3 2
3
3 3 4
3 3 3 5 4 5
s[i][j]
3
自底向上的填表算法
输入:n个连乘矩阵的大小p0,p1,…,pn 输出:最少乘法次数记为m[1][n],最优解s 算法(思路:自底向上,每行自左向右 填表) 1.for (i=n;i>=1;i--) //从倒数第一行开始填表 1.1 for(j=i;j<=n;j++) //计算每行的m[i][j](i<=j) 1.1.1 若i==j,m[i][j]=0; 否则转 1.1.2 1.1.2 计算断点在 i 处的乘法次数,记为当前的最优 值m[i][j];当前的最优断点为i,记入s[i][j]; 1.1.3 取断点值k=i+1到j-1: 计算断点为 k 处的乘法次数,与已有的最优值 m[i][j] 比较,如果当前值更优,则替换 m[i][j] 和最优断点s[i][j];
动态规划法求解步骤4——构造最优解
通过MatrixChain 的计算,我们知道要计算所给 的矩阵连乘积所需的最少数乘次数,但还不知道具 体应该按什么次序来做矩阵乘法才能达到最少的数 乘次数。 s[i][j]记录矩阵链A[i:j]的最佳断点。
对矩阵连乘问题构造最优解
void print(int i , int j , int **s) { if ( i == j ) cout << "A" << i; else { cout << "(" ; 构造最优解的时间复杂性:O(n) print (i,s[i][j],s); print (s[i][j]+1,j,s); cout << ")" ; } }
s[i][j] = i; for (int k = i+1; k < j; k++) { int t = LookupChain(i,k) + LookupChain(k+1,j) + p[i-1]*p[k]*p[j]; if (t < m[i][j]) { m[i][j] = t; s[i][j] = k;} } return m[i][j] ;
动态规划法求解步骤2——建立递归关系
设: (1) m[i][j](1≤i, j≤n) = 计算A[i, j] 的最少乘次数
(2) m[1][n] =计算A[1,n] 的最少乘次数。
(3) (A1A2…Ak)×(Ak+1…An)是 A[1, n]的其中 一个最优解。 则 相应的代价方程为:
K到底是多少呢?? 只好从1尝试到 n-1
动态规划法求解步骤2——建立递归关系
因为要尝试所有的k,因此递归方程为: 0 m[i][j] =
i≤k<j
i=j i<j
min{ m[i][k] + m[k+1][j] + pi–1pkpj }
直接递归求解
直接递归的时间复杂性
RecurMatrxChain的时间复杂性为: T(n) ≥ 1 + ∑(T(k) + T(n–k) + 1)
k=1
n–1
解此递归式,得: T(n)≥2n–1 = Ω(2n)。
此算法的时间复杂性随n的指数增长。不可行! 因为直接递归中有大量重复计算!
如A[1: 4]计算中(图中红框标出的都是重复计算):
1: 4
1: 2
1: 1 2: 4 1:1 2: 2
3: 4 3: 3 4: 4 1: 1 2: 2 2: 3 3: 3 1: 3 4: 4
C1 B1 A B2 C3 D3 C4 阶段0 阶段1 阶段2 阶段3 阶段4 C2 D1 D2 E
算法总体思想
动态规划算法与分治法类似,其基本思想也是将 待求解问题分解成若干个子问题。
T(n)
=
n
T(n/2)
T(n/2)
T(n/2)
T(n/2)
算法总体思想
但是经分解得到的子问题往往不是互相独立的。不 同子问题的数目常常只有多项式量级。在用分治法求解 时,有些子问题被重复计算了许多次。
m[i][i] = 计算A[i:i]的最少乘法次数 = 0
m[i][j] = m[i, k] + m[k+1, j] + pi-1pkpj
其中矩阵Ai(1≤i≤n)的维数为pi–1×pi,A[i:k]和A[k+1:j] 分别是pi-1 × pk和pk × pj矩阵。 pi-1pkpj是计算A[i:k] 和 A[k+1:j] 所需乘法数,
}
MemoizedMatrixChain(备忘录方式)的时间复杂性 时 间 复 杂 度 分 析 : 共 有 O(n2) 个 备 忘 录 记 录 项 m[i][j](1<=i<=j<=n), 初始化需要 O(n2) 时间。而 每个记录项只填入一次,每次耗时O(n)。故备忘录
方式的时间复杂度为O(n3) 。 空间复杂度分析:与自底向上的填表方式一样
方法二:自上而下 + 备忘录的求解方式——备忘录
算法
根据计算最优值时得到的信息,构造最优解。
3.1 矩阵连乘问题
给定n个矩阵:A1, A2, …, An,其中Ai与Ai+1是可 乘的。确定一种连乘的顺序,使得矩阵连乘的计算 量为最小。 设 A 和 B 分别是 p×q 和 q×r 的两个矩阵,则乘积 C=AB为p×r的矩阵,计算量为p*q*r次数乘。 但是对于多于 2 个以上的矩阵连乘,连乘的顺序 却非常重要,因为不同的顺序的总计算量将会有很 大的差别。
第三章 动态规划
引例 - 多阶段图最短路问题
下图表示城市之间的交通路网,线段上的数字表示 费用,单向通行由A->E。求A->E的最省费用。
C1
8 2 A 5 B2 2 B1 11 6 3 C2 C3 C4
4
2
8 D1 3 1
7 5
3
D2
D3
E
5
引例 - 多阶段图最短路问题
此图有明显的次序,可以划分为5阶段。故此问题的要求是:在 各个阶段选取一个恰当的决策,使由这些决策组成的一个决策序 列所决定的一条路线,其总路程最短。
不同计算顺序的差别
求多个矩阵的连乘积时,计算的结合顺序是十分重要的。
对穷举搜索法的思考
穷举法:列举出所有可能的计算次序,并计算 出每一种计算次序相应需要的数乘次数,从中找出 一种数乘次数最少的计算次序。 n个矩阵的连乘积至少有 (4n/n3/2)个不同的计算顺 序,即计算顺序随 n 的增长呈指数增长。因而穷举 搜索法对此问题不是有效的算法。
事实正是如此。为什么? (通过证明知道:P46 反证法)
动态规划法求解步骤1——分析最优解的结构
矩阵连乘计算次序问题的最优解包含着其子问 题的最优解。这种性质称为最优子结构性质。问题
的最优子结构性质保证了该问题使用动态规划求解
的正确性。
继续分析 子问题的重叠性 :
具有 子问题重叠性 !! 重叠子问题性质保证了该问题使用动态规划求解的 有效性。
使用动态规划技术的问题特征
最优子结构性质 当一个问题的最优解包含了其子问题的最优 解时,我们说这个问题具有最优子结构。 重叠子问题性质 在问题的求解过程中,很多子问题的解会被
多次使用。
动态规划基本步骤
找出最优解的性质,并刻划其结构特征。
递归地定义最优值。
计算出最优值。
方法一:自底向上的求解方式——动态规划算法
s[1,6]=3 [1,3] [1,1] [2,3] [4,5] [6,6] [2,2] [3,3] [4,4] [5,5] (A1(A2A3))((A4A5)A6)
[4,6]
动态规划算法的基本方法
动态规划算法通常可以按以下几个步骤进行: 找出最优解的性质,并刻画其结构特征; 递归地定义最优值; 以备忘录方式或自底向上方式计算出各子结构 的最优值; 根据计算最优值时得到的信息,构造最优解。 步骤 1~3 是动态规划算法的基本步骤。若需要最 优解,则必须执行第4步,为此还需要在第3步中记 录构造最优解所必需的信息。
自底向上填表的矩阵连乘算法
void MatrixChain(int p, int n, int **m, int **s) { for (int i=n;i>=1;i-- ) for (int j=i;j<=n;j++ ) { //断点为i if (i==j) { m[i][j]=0; continue; } m[i][j] = m[i][i] + m[i+1][j]+p[i–1]*p[i]*p[j]; s[i][j] = i; for (int k = i + 1; k < j; k++) { //k为断点, i<k<j int t = m[i][k] + m[k+1][j] + p[i–1]*p[k]*p[j]; if (t < m[i][j]) {m[i][j] = t; s[i][j] = k;} //记下较小的m[i][j]及相应的断点k }}}
备忘录算法:P51
int MemoizedMatrixChain(int n,int **m,int **s) 备忘录算法的控制结构与直接递归方法的控制结构相 { 同,区别在于备忘录算法为每个解过的子问题建立了备忘 for(int i=1;i<=n;i++) 录以备需要时查看,避免了相同子问题的重复求解。 int LookupChain(int i,int j,int **m , int **s) for(j=i;i<=n;j++) m[i][j]=0; { //初始化,标记为对应子问题尚未求解 if (m[i][j] > 0) return m[i][j]; return if (i == j) LookupChain(1,n,m,s); return 0; } int m[i][j] = LookupChain(i,i) + LookupChain(i+1,j) + p[i-1]*p[i]*p[j];
MatrixChain(自底向上填表)的时间复杂性
算法MatrixChain的主要计算取决于程序中对 i、 j和k的三重循环。循环体内的计算量为O(1),1≤ i、 j 、 k≤n ,三重循环的总次数为 O(n3) 。因此该算法 时间复杂性的上界为O(n3) 。算法使用空间显然为 O(n2)。 动态规划算法的基本要素: 这种算法称为动态规划算法。 • 最优子结构性质
1 1 2 3 4 5 6
0
2
0
3
2625
4
5
6
1 2 3 4 5 6
15750 7875 0
9375 11875 15125 4375 6000 7125 10500
750 0 2500 1000 5375 6250 3500 5000 0
• 重叠子问题性质
计算最优值方法2:递归+备忘录
备忘录方法采用一些表格来保存已解子问题的 信息(最优值、相应最优解的线索等)。每个子问 题初始化时都标记为尚未求解。在递归求解过程中, 对每个待解子问题,先查看它是否已求解。若未求 解,则计算其解并填表保存。若已求解,则查表取 出相应的结果。 书中把自上而下+备忘录的方法称为备忘录算法。
动态规划法求解步骤1——分析最优解的结构
思考:将矩阵连乘积AiAi+1…Aj记为A[i: j]。 设 A[1: n] 的其中一个最优解在矩阵 Ak 和 Ak+1 处断 开,即 A[1:n] = ( A[1:k] )×( A[k+1:n] )。
若 矩阵连乘问题具备最优子结构性质,则 : A[1: k] 和 A[k+1: n] 在 A[1:n]的最优解中的加 括号方式也分别是子问题 A1A2…AK 和 Ak+1Ak+2…An 的其中一个最优解。
2: 2
3: 3
3: 4
4: 4
2: 3
2: 2
4: 4
3: 3
1:来自百度文库2 1: 1
3: 3 2: 2
动态规划法求解步骤3——计算出最优值
计算最优值的两个方法: 1、自底向上 的 填表 方式 2、自顶向下 的 递归+备忘录 方式 该方式因为要避免重复计算同一子问题,所 以要引入备忘录。
计算最优值方法1:自底向上的填表方法
3.3 最长公共子序列(LCS)
若 给 定 序 列 X={x1,x2,…,xm} , 则 另 一 序 列 Z={z1,z2,…,zk} ,是 X 的子序列是指存在一个严格递
增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:
zj=xij。例如,序列Z={B,C,D,B}是序列X={A,B,C, B , D , A ,B} 的子序列,相应的递增下标序列为 {2 , 3 , 5,7}。 给定 2 个序列 X 和 Y ,当另一序列 Z 既是 X 的子序列又 是Y的子序列时,称Z是序列X和Y的公共子序列。 给 定 2 个 序 列 X={x1,x2,…,xm} 和 Y={y1,y2,…,yn} , 找出X和Y的最长公共子序列。
m[i][j]
0
1 2 3 4 5 6
1
1
2
3 3 2
3
3 3 4
3 3 3 5 4 5
s[i][j]
3
自底向上的填表算法
输入:n个连乘矩阵的大小p0,p1,…,pn 输出:最少乘法次数记为m[1][n],最优解s 算法(思路:自底向上,每行自左向右 填表) 1.for (i=n;i>=1;i--) //从倒数第一行开始填表 1.1 for(j=i;j<=n;j++) //计算每行的m[i][j](i<=j) 1.1.1 若i==j,m[i][j]=0; 否则转 1.1.2 1.1.2 计算断点在 i 处的乘法次数,记为当前的最优 值m[i][j];当前的最优断点为i,记入s[i][j]; 1.1.3 取断点值k=i+1到j-1: 计算断点为 k 处的乘法次数,与已有的最优值 m[i][j] 比较,如果当前值更优,则替换 m[i][j] 和最优断点s[i][j];
动态规划法求解步骤4——构造最优解
通过MatrixChain 的计算,我们知道要计算所给 的矩阵连乘积所需的最少数乘次数,但还不知道具 体应该按什么次序来做矩阵乘法才能达到最少的数 乘次数。 s[i][j]记录矩阵链A[i:j]的最佳断点。
对矩阵连乘问题构造最优解
void print(int i , int j , int **s) { if ( i == j ) cout << "A" << i; else { cout << "(" ; 构造最优解的时间复杂性:O(n) print (i,s[i][j],s); print (s[i][j]+1,j,s); cout << ")" ; } }
s[i][j] = i; for (int k = i+1; k < j; k++) { int t = LookupChain(i,k) + LookupChain(k+1,j) + p[i-1]*p[k]*p[j]; if (t < m[i][j]) { m[i][j] = t; s[i][j] = k;} } return m[i][j] ;
动态规划法求解步骤2——建立递归关系
设: (1) m[i][j](1≤i, j≤n) = 计算A[i, j] 的最少乘次数
(2) m[1][n] =计算A[1,n] 的最少乘次数。
(3) (A1A2…Ak)×(Ak+1…An)是 A[1, n]的其中 一个最优解。 则 相应的代价方程为:
K到底是多少呢?? 只好从1尝试到 n-1
动态规划法求解步骤2——建立递归关系
因为要尝试所有的k,因此递归方程为: 0 m[i][j] =
i≤k<j
i=j i<j
min{ m[i][k] + m[k+1][j] + pi–1pkpj }
直接递归求解
直接递归的时间复杂性
RecurMatrxChain的时间复杂性为: T(n) ≥ 1 + ∑(T(k) + T(n–k) + 1)
k=1
n–1
解此递归式,得: T(n)≥2n–1 = Ω(2n)。
此算法的时间复杂性随n的指数增长。不可行! 因为直接递归中有大量重复计算!
如A[1: 4]计算中(图中红框标出的都是重复计算):
1: 4
1: 2
1: 1 2: 4 1:1 2: 2
3: 4 3: 3 4: 4 1: 1 2: 2 2: 3 3: 3 1: 3 4: 4
C1 B1 A B2 C3 D3 C4 阶段0 阶段1 阶段2 阶段3 阶段4 C2 D1 D2 E
算法总体思想
动态规划算法与分治法类似,其基本思想也是将 待求解问题分解成若干个子问题。
T(n)
=
n
T(n/2)
T(n/2)
T(n/2)
T(n/2)
算法总体思想
但是经分解得到的子问题往往不是互相独立的。不 同子问题的数目常常只有多项式量级。在用分治法求解 时,有些子问题被重复计算了许多次。
m[i][i] = 计算A[i:i]的最少乘法次数 = 0
m[i][j] = m[i, k] + m[k+1, j] + pi-1pkpj
其中矩阵Ai(1≤i≤n)的维数为pi–1×pi,A[i:k]和A[k+1:j] 分别是pi-1 × pk和pk × pj矩阵。 pi-1pkpj是计算A[i:k] 和 A[k+1:j] 所需乘法数,
}
MemoizedMatrixChain(备忘录方式)的时间复杂性 时 间 复 杂 度 分 析 : 共 有 O(n2) 个 备 忘 录 记 录 项 m[i][j](1<=i<=j<=n), 初始化需要 O(n2) 时间。而 每个记录项只填入一次,每次耗时O(n)。故备忘录
方式的时间复杂度为O(n3) 。 空间复杂度分析:与自底向上的填表方式一样
方法二:自上而下 + 备忘录的求解方式——备忘录
算法
根据计算最优值时得到的信息,构造最优解。
3.1 矩阵连乘问题
给定n个矩阵:A1, A2, …, An,其中Ai与Ai+1是可 乘的。确定一种连乘的顺序,使得矩阵连乘的计算 量为最小。 设 A 和 B 分别是 p×q 和 q×r 的两个矩阵,则乘积 C=AB为p×r的矩阵,计算量为p*q*r次数乘。 但是对于多于 2 个以上的矩阵连乘,连乘的顺序 却非常重要,因为不同的顺序的总计算量将会有很 大的差别。
第三章 动态规划
引例 - 多阶段图最短路问题
下图表示城市之间的交通路网,线段上的数字表示 费用,单向通行由A->E。求A->E的最省费用。
C1
8 2 A 5 B2 2 B1 11 6 3 C2 C3 C4
4
2
8 D1 3 1
7 5
3
D2
D3
E
5
引例 - 多阶段图最短路问题
此图有明显的次序,可以划分为5阶段。故此问题的要求是:在 各个阶段选取一个恰当的决策,使由这些决策组成的一个决策序 列所决定的一条路线,其总路程最短。
不同计算顺序的差别
求多个矩阵的连乘积时,计算的结合顺序是十分重要的。
对穷举搜索法的思考
穷举法:列举出所有可能的计算次序,并计算 出每一种计算次序相应需要的数乘次数,从中找出 一种数乘次数最少的计算次序。 n个矩阵的连乘积至少有 (4n/n3/2)个不同的计算顺 序,即计算顺序随 n 的增长呈指数增长。因而穷举 搜索法对此问题不是有效的算法。
事实正是如此。为什么? (通过证明知道:P46 反证法)
动态规划法求解步骤1——分析最优解的结构
矩阵连乘计算次序问题的最优解包含着其子问 题的最优解。这种性质称为最优子结构性质。问题
的最优子结构性质保证了该问题使用动态规划求解
的正确性。
继续分析 子问题的重叠性 :
具有 子问题重叠性 !! 重叠子问题性质保证了该问题使用动态规划求解的 有效性。
使用动态规划技术的问题特征
最优子结构性质 当一个问题的最优解包含了其子问题的最优 解时,我们说这个问题具有最优子结构。 重叠子问题性质 在问题的求解过程中,很多子问题的解会被
多次使用。
动态规划基本步骤
找出最优解的性质,并刻划其结构特征。
递归地定义最优值。
计算出最优值。
方法一:自底向上的求解方式——动态规划算法
s[1,6]=3 [1,3] [1,1] [2,3] [4,5] [6,6] [2,2] [3,3] [4,4] [5,5] (A1(A2A3))((A4A5)A6)
[4,6]
动态规划算法的基本方法
动态规划算法通常可以按以下几个步骤进行: 找出最优解的性质,并刻画其结构特征; 递归地定义最优值; 以备忘录方式或自底向上方式计算出各子结构 的最优值; 根据计算最优值时得到的信息,构造最优解。 步骤 1~3 是动态规划算法的基本步骤。若需要最 优解,则必须执行第4步,为此还需要在第3步中记 录构造最优解所必需的信息。
自底向上填表的矩阵连乘算法
void MatrixChain(int p, int n, int **m, int **s) { for (int i=n;i>=1;i-- ) for (int j=i;j<=n;j++ ) { //断点为i if (i==j) { m[i][j]=0; continue; } m[i][j] = m[i][i] + m[i+1][j]+p[i–1]*p[i]*p[j]; s[i][j] = i; for (int k = i + 1; k < j; k++) { //k为断点, i<k<j int t = m[i][k] + m[k+1][j] + p[i–1]*p[k]*p[j]; if (t < m[i][j]) {m[i][j] = t; s[i][j] = k;} //记下较小的m[i][j]及相应的断点k }}}
备忘录算法:P51
int MemoizedMatrixChain(int n,int **m,int **s) 备忘录算法的控制结构与直接递归方法的控制结构相 { 同,区别在于备忘录算法为每个解过的子问题建立了备忘 for(int i=1;i<=n;i++) 录以备需要时查看,避免了相同子问题的重复求解。 int LookupChain(int i,int j,int **m , int **s) for(j=i;i<=n;j++) m[i][j]=0; { //初始化,标记为对应子问题尚未求解 if (m[i][j] > 0) return m[i][j]; return if (i == j) LookupChain(1,n,m,s); return 0; } int m[i][j] = LookupChain(i,i) + LookupChain(i+1,j) + p[i-1]*p[i]*p[j];
MatrixChain(自底向上填表)的时间复杂性
算法MatrixChain的主要计算取决于程序中对 i、 j和k的三重循环。循环体内的计算量为O(1),1≤ i、 j 、 k≤n ,三重循环的总次数为 O(n3) 。因此该算法 时间复杂性的上界为O(n3) 。算法使用空间显然为 O(n2)。 动态规划算法的基本要素: 这种算法称为动态规划算法。 • 最优子结构性质