[XJTUSE 算法设计与分析] 第三章 动态规划 3.2 动态规划典型例题与解题思路(一)
文章目录
- 3.2 动态规划典型例题与解题思路(一)
- 一、拆分类
- 1、矩阵连乘(极重要)
- 1)穷举法
- 2)动态规划法
- 3)备忘录方法
- 2、凸多边形最优三角剖分
- ① 三角剖分的结构
- ② 最优子结构性质
- ③ 最优三角形剖分的递归结构
- ④ 计算最优值
- ⑤ 构造最优三角剖分
- 3、多边形游戏
- 4、公园游艇问题(考试难度类似)
3.2 动态规划典型例题与解题思路(一)
分类是为了便于初学者理解,当熟练以后就没必要分开来看,都是动态规划。
一、拆分类
该类问题子问题比较清楚与起始终止位置无关而与自身长度有关。很容易看出“积木所在”通过组合下一级构造上一级。
1、矩阵连乘(极重要)
给定n个矩阵{A1,A2,…,An}\{A_1, A_2, …, A_n\}{A1,A2,…,An},其中AiA_iAi与Ai+1A_{i+1}Ai+1是可乘的,i=1, 2,…, n-1。考察这n个矩阵的连乘积A1A2…AnA_1A_2…A_nA1A2…An。
由于矩阵乘法满足结合律,所以计算矩阵的连乘可以有许多不同的计算次序。这种计算次序可以用加括号的方式来确定。若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。
完全加括号的矩阵连乘积可递归地定义为:
①单个矩阵是完全加括号的;
②矩阵连乘积A是完全加括号的,则A可表示为2个完全加括号的矩阵连乘积B和C的乘积并加括号,即A=(BC)
设有四个矩阵A,B,C,D,可以有以下5种不同的加括号方式:
(A(B(CD))) (A((BC)D)) ((AB)(CD)) ((A(BC))D) (((AB)C)D)
每一种完全加括号方式对应于一种矩阵连乘积的计算次序,而矩阵连乘积的计算次序与其计算量有密切关系。矩阵A(p×q)和矩阵B(q×r)的乘积C=AB是一个p×r的矩阵,数乘次数为pqr
❓ 问题:
给定n个矩阵{A1,A2,…,An}\{A_1, A_2, …, A_n\}{A1,A2,…,An},其中AiA_iAi与Ai+1A_{i+1}Ai+1是可乘的,i=1, 2,…, n-1。如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
1)穷举法
计算次序相应需要的数乘次数,从中找出一种数乘次数最少的计算次序。
对于n个矩阵的连乘积,设其不同的计算次序为P(n)。由于每种加括号方式都可以分解为两个子矩阵的加括号问题:(A1…Ak)(Ak+1…An)可以得到关于P(n)的递推式如下:
2)动态规划法
将矩阵连乘积AiAi+1…Aj简记为A[i:j] ,这里i≤j 。
考察计算A[i:j]的最优计算次序。设这个计算次序在矩阵Ak和Ak+1之间将矩阵链断开,i≤k<j,则其相应完全加括号方式为:
(AiAi+1…Ak)(Ak+1Ak+2…Aj)( A_iA_{i+1}…A_k)(A_{k+1}A_{k+2}…A_j)(AiAi+1…Ak)(Ak+1Ak+2…Aj)
计算量:A[i:k]的计算量加上A[k+1:j]的计算量,再加上A[i:k]和A[k+1:j]相乘的计算量。
1️⃣ 分析最优解的结构
特征:计算A[i:j]的最优次序所包含的计算矩阵子链 A[i:k]和A[k+1:j]的次序也是最优的。
矩阵连乘计算次序问题的最优解包含着其子问题的最优解。这种性质称为最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法求解的显著特征。
2️⃣ 建立递归关系
设计算A[i:j],1≤i≤j≤n,所需要的最少数乘次数m[i,j],则原问题的最优值为m[1,n]。
设Ai的维数为pi−1×pip_{i-1}×p_ipi−1×pi,则
当i=j时,A[i:j]=AiA[i:j]=AiA[i:j]=Ai,因此,m[i,i]=0,i=1,2,…,n
当i<j时,m[i:j]=m[i,k]+m[k+1,j]+pi−1pkpjm[i:j]=m[i,k]+m[k+1,j]+p_{i-1}p_kp_jm[i:j]=m[i,k]+m[k+1,j]+pi−1pkpj
可以递归地定义m[i,j]为:
k的位置只有j-i种可能
3️⃣ 计算最优值
对于1≤i≤j≤n不同的有序对(i,j)对应于不同的子问题。因此,不同子问题的个数最多只有
由此可见,在递归计算时,许多子问题被重复计算多次。这也是该问题可用动态规划算法求解的又一显著特征
用动态规划算法解此问题,可依据其递归式以自底向上的方式进行计算。在计算过程中,保存已解决的子问题答案。每个子问题只计算一次,而在后面需要时只要简单查一下,从而避免大量的重复计算,最终得到多项式时间的算法。
代码实现如下:
void matrixMutiply(int[] p, int n, int[][] m, int[][] s) {for (int i = 1; i <= n; i++) {//单一矩阵,无需计算m[i][i] = 0;}for (int r = 2; r <= n; r++) {//对角线以上开始计算for (int i = 1; i <= n - r + 1; i++) {int j = r + i - 1;//找m[i][j]的最小值,先初始化一下,令k=im[i][j] = m[i + 1][j] + p[i - 1] * p[i] * p[j];s[i][j] = i;//记录分割点//k从i+1到j-1循环找m[i][j]的最小值for (int k = i + 1; k < j; k++) {//k为分割点int temp = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];if (temp < m[i][j]) {m[i][j] = temp;s[i][j] = k;}}}}}
算法复杂性分析:
算法MatrixChain的主要计算量取决于算法中对r,i和k的3重循环。循环体内的计算量为O(1)O(1)O(1),而3重循环的总次数为O(n3)O(n^3)O(n3)。因此算法的计算时间上界为O(n3)O(n^3)O(n3)。算法所占用的空间显然为O(n2)O(n^2)O(n2)。
4️⃣ 构造最优解
由s[1][n]的值可知A[1:n]的最优加括号方式为(A[1:s[1][n]])(A[s[1][n]+1:n]),以此递推由s[1][n]的值可知A[1:n]的最优加括号方式为(A[1:s[1][n]])(A[s[1][n]+1:n]),以此递推由s[1][n]的值可知A[1:n]的最优加括号方式为(A[1:s[1][n]])(A[s[1][n]+1:n]),以此递推
void traceBack(int i, int j, int[][] s) {if (i == j) {System.out.print("A" + i);} else {System.out.print("(");traceBack(i, s[i][j], s);traceBack(s[i][j] + 1, j, s);System.out.print(")");}}public static void main(String[] args) {int[] p = {30, 35, 15, 5, 10, 20, 25};int n = 6;int[][] m = new int[7][7];int[][] s = new int[7][7];MatrixChain test = new MatrixChain();test.matrixMutiply(p, n, m, s);test.traceBack(1, 6, s);System.out.println();System.out.println(m[1][6]);}
}
运算结果:
((A1(A2A3))((A4A5)A6))
15125
3)备忘录方法
备忘录方法的控制结构与直接递归方法的控制结构相同,区别在于备忘录方法为每个解过的子问题建立了备忘录以备需要时查看,避免了相同子问题的重复求解。
备忘录方法的递归方式是自顶向下的,而动态规划算法则是自底向上递归的。
int MemoizedMatrixChain(int []p,int n,int [][]m,int [][]s)
{for(int i=1;i<=n;i++)for(int j=i;j<=n;j++)m[i][j]=0; //0表示相应的子问题还末被计算return LookupChain(1,n,p,m,s);
}
int LookupChain(int i,int j,int[]p,int[][]m,int [][]s)
{if(m[i][j]>0) //大于0表示其中存储的是所要求子问题的计算结果return m[i][j]; //直接返回此结果即可if(i==j)return 0;int u=LookupChain(i,i,p,m,s)+LookupChain(i+1,j,p,m,s)+p[i-1]*p[i]*p[j];s[i][j]=i;for(int k=i+1;k<j;k++){int t=LookupChain(i,k,p,m,s)+LookupChain(k+1,j,p,m,s)+p[i-1]*p[k]*p[j];if(t<u){u=t;s[i][j]=k;}}m[i][j]=u;return u;
}
一般来讲,当一个问题的所有子问题都至少要解一次时,用动态规划算法比用备忘录方法好;当子问题空间中的部分子问题可不必求解时,用备忘录方法则较有利。
2、凸多边形最优三角剖分
用多边形顶点的逆时针序列表示凸多边形,即P={v0,v1,…,vn-1}表示具有n条边的凸多边形。
若vi与vj是多边形上不相邻的2个顶点,则线段vivj称为多边形的一条弦。弦将多边形分割成2个多边形{vi,vi+1,…,vj}和{vj,vj+1,…,vi}。
多边形的三角剖分是将多边形分割成互不相交的三角形的弦的集合T。
注意:T中各弦互不相交,且集合T已达到最大;有n个顶点的凸多边形的三角剖分中,恰有n-3条弦和n-2个三角形。
题目描述:给定凸多边形P,以及定义在由多边形的边和弦组成的三角形上的权函数w。要求确定该凸多边形的三角剖分,使得该三角剖分中诸三角形上权之和为最小。
① 三角剖分的结构
一个表达式的完全加括号方式相应于一棵完全二叉树,称为表达式的语法树。例如,完全加括号的矩阵连乘积((A1(A2A3))(A4(A5A6)))所相应的语法树如图 (a)所示。凸多边形{v0,v1,…vn-1}的三角剖分也可以用语法树表示。例如,图 (b)中凸多边形的三角剖分可用图 (a)所示的语法树表示。
矩阵连乘积中的每个矩阵Ai对应于凸(n+1)边形中的一条边vi−1viv_{i-1}v_ivi−1vi。三角剖分中的一条弦vivjv_iv_jvivj,i<j,对应于矩阵连乘积A[i+1:j]。
给定矩阵链A1A2A3A4A5A6A_1A_2A_3A_4A_5A_6A1A2A3A4A5A6,Ai的维数为pi−1×pip_{i-1}×p_ipi−1×pi;定义凸多边形P={v0,v1,v2,v3,v4,v5,v6v_0,v_1,v_2,v_3,v_4,v_5,v_6v0,v1,v2,v3,v4,v5,v6},其三角形vivjvkv_iv_jv_kvivjvk上的权函数值为w(vivjvk)=pipjpkw(v_iv_jv_k)=p_ip_jp_kw(vivjvk)=pipjpk,依此定义,P的最优三角剖分所对应的语法树给出了矩阵链的最优完全加括号方式。
② 最优子结构性质
凸多边形的最优三角剖分问题有最优子结构性质。
事实上,若凸(n+1)边形P={v0,v1,…,vn-1}的最优三角剖分T包含三角形v0vkvn,1≤k≤n-1,则T的权为3个部分权的和:三角形v0vkvn的权,子多边形{v0,v1,…,vk}和{vk,vk+1,…,vn}的权之和。可以断言,由T所确定的这2个子多边形的三角剖分也是最优的。因为若有{v0,v1,…,vk}或{vk,vk+1,…,vn}的更小权的三角剖分将导致T不是最优三角剖分的矛盾。
③ 最优三角形剖分的递归结构
定义t[i][j]t[i][j]t[i][j],1≤i<j≤n为凸子多边形{vi-1, vi,…,vj}的最优三角剖分所对应的权函数值,即其最优值。为方便起见,设退化的多边形{vi-1,vi}具有权值0。据此定义,要计算的凸(n+1)边形P的最优权值为t[1][n]t[1][n]t[1][n]。
t[i][j]t[i][j]t[i][j]的值可以利用最优子结构性质递归地计算。当j-i≥1时,凸子多边形至少有3个顶点。由最优子结构性质,**t[i][j]t[i][j]t[i][j]**的值应为t[i][k]t[i][k]t[i][k]的值加上t[k+1][j]t[k+1][j]t[k+1][j]的值,再加上三角形vi−1vkvjv_{i-1}v_kv_jvi−1vkvj的权值,其中i≤k≤j-1。由于在计算时还不知道k的确切位置,而k的所有可能位置只有j-i个,因此可以在这j-i个位置中选出使值t[i][j]t[i][j]t[i][j]达到最小的位置。由此,t[i][j]t[i][j]t[i][j]可递归地定义为
④ 计算最优值
template<typename Type,typename E>
void MinWeightTriangulation(E *p,int n,Type **t,int **s)
{for(int i=1; i<=n; i++)t[i][i]=0;for(int r=2; r<=n; r++)for(int i=1; i<=n-r+1; i++){int j=i+r-1;t[i][j]=t[i+1][j]+w(p,i-1,i,j);s[i][j]=i; //k=ifor(int k=i+1; k<i+r-1; k++){int u=t[i][k]+t[k+1][j]+w(p,i-1,k,j);if(u<t[i][j]){t[i][j]=u;s[i][j]=k;}}}
}
⑤ 构造最优三角剖分
s[i][j]s[i][j]s[i][j]记录了与vi-1和vj一起构成三角形的第3个顶点的位置,据此可构造出最优三角剖分中的所有三角形。
template<typename E>
void Traceback(E *p,int i,int j,int **s)
{if(i==j)return;int k=s[i][j];cout<<p[i-1].v<<" "<<p[k].v<<" "<<p[j].v<<endl;Traceback(p,i,k,s);Traceback(p,k+1,j,s);
}
3、多边形游戏
多边形游戏是一个单人玩的游戏,开始时有一个由n个顶点构成的多边形。每个顶点被赋予一个整数值,每条边被赋予一个运算符“+”或“*”。所有边依次用整数从1到n编号。
1️⃣ 游戏第1步,将一条边删除。
2️⃣ 随后n-1步按以下方式操作:
(1)选择一条边E以及由E连接着的2个顶点V1和V2;
(2)用一个新的顶点取代边E以及由E连接着的2个顶点V1和V2。将由顶点V1和V2的整数值通过边E上的运算得到的结果赋予新顶点。
3️⃣ 最后,所有边都被删除,游戏结束。游戏的得分就是所剩顶点上的整数值。
4、公园游艇问题(考试难度类似)
题目描述:长江游艇俱乐部在长江上设置了n 个游艇出租站{1,2,…,n}。游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。游艇出租站 i 到游艇出租站 j 之间的租金为r(i,j),1≤i<j≤n。试设计一个算法,计算出从游艇出租站 1 到游艇出租站 n 所需的最少租金
1️⃣ 最优解结构
r(i,j)表示游艇出租站i直接到j之间的租金,m(i,j)表示从出租站i出发,到达第j站需要的租金
例如m(1,3)就表示从第1站出发,到达第3站所需的租金,而m(1,3)可以有多种租用方案,例如可以1-2.2-3与1-3。
假设在第k站换游艇(i≤k≤ji\le k\le ji≤k≤j) ,则有m(i,j)=m(i,k)+m(k,j),其中m(i,j)的最优解包括m(i,k)与m(k,j)的最优解。
2️⃣ 建立递归关系
由以上分析可知,显然有:
3️⃣ 计算最优值
void cent(int[][] m, int n, int[][] s) {for (int i = 1; i <=n ; i++) {m[i][i]=0;}for (int r = 2; r <= n; r++)for (int i = 1; i <= n - r + 1; i++) {int j = i + r - 1;s[i][j] = i;for (int k = i; k <= j; k++) {int temp = m[i][k] + m[k][j];if (temp < m[i][j]) {m[i][j] = temp;s[i][j] = k;//在第k站下}}}}
4️⃣ 构造最优解
void traceBack(int i, int j, int[][] s) {if (i == j) {System.out.print(i);return;}System.out.print("[");traceBack(i, s[i][j], s);traceBack(s[i][j] + 1, j, s);System.out.print("]");
}
[XJTUSE 算法设计与分析] 第三章 动态规划 3.2 动态规划典型例题与解题思路(一)相关推荐
- [XJTUSE 算法设计与分析] 第五章 回溯法
第五章 回溯法 填空题会有代码填空,大题会手动回溯 学习要点 理解回溯法的深度优先搜索策略. 掌握用回溯法解题的算法框架 (1)递归回溯 (2)迭代回溯 (3)子集树算法框架 (4)排列树算法框架 5 ...
- [算法设计与分析]第三章练习题:删除多余括号
问题描述 从键盘输入一个含有括号的四则运算表达式,要求去掉可能含有的多余的括号,结果要保持原表达式中变量和运算符的相对位置不变,且与原表达式等价,不要求化简.另外不考虑'+' '-'用作正负号的情况, ...
- 算法设计与分析第三章作业
一. 二. 动态规划最核心的思想,就在于拆分子问题,记住过往,减少重复计算. 动态规划问题的关键在于正确地写出基本的递推关系式和恰当的边界条件(也就是基本方程).要做到这一点,必须将问题的过程划分成几 ...
- 计算机算法设计与分析第五章思维导图知识点总结 ( 初稿 )
复习链接 计算机算法设计与分析第一章思维导图 计算机算法设计与分析第二章思维导图&&知识点总结 计算机算法设计与分析第三章思维导图&&知识点总结 计算机算法设计与分析第 ...
- 算法设计与分析第七章分支限界算法(完结篇)
算法设计与分析第七章分支限界算法 一.分支界限算法概述 1.分支限界法类似于回溯法,是一种在问题的解空间树上搜索问题解的算法. 分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解 ...
- 算法设计与分析第5章 回溯法(二)【回溯法应用】
第5章 回溯法 5.2 应用范例 1.0-1背包问题 有n件物品和一个容量为c的背包.第i件物品的重量是w[i],价值是p[i].求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和 ...
- 算法设计与分析基础 第一章谜题
习题1.1 10.b 欧几里得游戏 一开始,板上写有两个不相等的正整数,两个玩家交替写数字,每一次,当前玩家都必须在板上写出任意两个板上数字的差,而且这两个数字必须是新的,也就是说,不能与板上任何一个 ...
- 算法设计与分析 实验三 回溯法求解地图填色问题
回溯法求解地图填色问题 一.实验目的与要求 1.实验基本要求: 2.实验亮点: 二.实验内容与方法 三.实验步骤与过程 1.未优化的回溯: (1)算法描述: (2)编程实现 (3)运行并测试: 2.对 ...
- 算法设计与分析 实验三 贪心算法
一. 实验目的和要求 1.掌握贪心算法的基本思想. 2.学习利用贪心算法设计和实现算法的方法. 3.了解利用替换法证明贪心策略是否能获得全局最优解的过程. 4.熟练掌握贪心算法在两个典型图搜索中的应用 ...
最新文章
- 比特币大涨,如何辨别牛市是否真正到来?
- Java 加密解密 对称加密算法 非对称加密算法 MD5 BASE64 AES RSA
- html的学习小结(3):HTML 4.0 事件属性
- 平板电脑办公软件_大屏平板互动软件-平板电脑触摸大屏控制软件
- ubuntu修改服务器端口,服务器环境之4:ubuntu14.04安装tomcat,端口修改
- linux新建samba账户,ubuntu上创建账户和samba用户
- 在Window下编译OpenH323
- 我认为的android入门学习策略
- php rgb,php颜色转换函数hex-rgb
- 本次谈谈罕见的三方数据维度的cut-off切分,你肯定没遇过
- 折线分割平面(递推dp)
- Java-实现图书管理系统
- php屏蔽中文浏览器,网站屏蔽中文浏览器/英文浏览器方法
- 根据先序遍历建立一个二叉树
- 影之刃服务器维护,影之刃无法联机到服务器怎么办 解决办法
- 单词首字母变大写-vue
- ****you do not have the permissions necessary t...
- 【字符串】Hash表
- Google Code注册方法详解 Google Code网盘申请方法 1
- qq拼音输入法引起的ctrl键粘滞问题