动态规划
1、问题建模;确定优化函数;约束条件
2、划分子问题
3、递推方程;也就是原问题的优化函数与子问题的优化函数存在的依赖关系;
4、是否满足组优化原则,也就是满足最优子结构性质
5、界定最小子问题,以及其优化函数初值等于什么;
如果采用递归的计算,相同子问题很有可能会计算多次,导致复杂度任然不低;要降低复杂度,需要保证每个相同的子问题只计算一次
6、从最小子问题算起,明确计算顺序,保证后面用到的值已经算好
7、保存子问题的计算结果–备忘录
8、是否需要设计标记函数
9、追踪解;如何得到解
老师以最短路径问题引出动态规划
P36 动态规划的例子
最短路径问题
一个实例
蛮力算法:
穷举所有可能的起点到终点的路径;
第一个疑惑:用什么数据结构描述问题输入
定义子问题
有五层节点,求最短路径
可以定义子问题,c节点到T节点的最短路径
容易知道,c1到T节点,必然是走上面的2短,c2必然走下面的3更短,依次类推可以轻松得到C层的各节点到T节点的最短路径
那么C和T层可以合并
再划分子问题,B层到T层的最短路径;依次可以得出
继续划分子问题直到求S层到T层
子问题界定
为什么能用动态规划,因为子问题的解,与原问题的解存在依赖关系
举一个反例子
破坏了
此时,此例子的动态规划其实也可以理解为时分治算法,并不是一个典型的动态规划算法;只是为了引出问题。引出下面对动态规范算法设计的思考
P37 动态规划算法设计
需要考虑以下问题
举个栗子
介绍一下矩阵相乘
也就是矩阵A(i,j)与矩阵B(j,k)
矩阵的行数要等于列数,相乘后的结果A(i,j)*B(j,k) = C(i,k)
需要做的乘法运算为 i * j * k;
1、问题建模,把矩阵链的数据结构表示成数组,p0-pn 一共有n个矩阵,p0是第一个矩阵的行,p1是第一个矩阵的列也是第二个矩阵的行;
p0 p1 p2 p3 p4 p5 p6 p7 p8
假如第一个矩阵与第二个矩阵相乘则,乘法次数为p0 * p1 * p2
并且变换为新的矩阵,p0 p2;
优化的目标是使得乘法次数ijk最小
蛮力算法
蛮力算法也就是穷举出所有可能的矩阵相乘顺序次数的结果
相当于在矩阵直接加括号,加了括号的优先相乘
蛮力算法我也不知道怎么去穷举
我能想到的是,数组《p0,p1,…,pn》
构建两个栈,依次入栈;
初始化两个栈。左栈入p0,右栈入p1
然后依次入栈,也就是一边放一个,轮着来;
然后随机出栈,出栈时,两个都出栈做相乘运算,相当于加括号;
当任意栈空时,强制入栈,不当做一种入栈的顺序;
这样就两种操作,出栈和入栈,列出所有的出栈和入栈顺序就相当于穷举;
递归的二选一,得到所有的出栈和入栈序列;实现穷举
指数级复杂度;
动态规划算法
子问题划分
P38 动态规划算法递归实现
可以发现通过动态规划,复杂度有所下降,但仍是不理想;
在此之前的算法设计仍然可以归结是一个分治算法,没有动态规划算法的关键
分析算法
假设最初的问题,规模是5
第一层,根据k的位置,将会产生4个子问题组合,相当于8个子问题。
图中相同颜色的框框代表相同的子问题,可以发现相同的子问题会重复出现,重复的计算导致复杂度很高
同一个子问题被多次计算
如何改进
P39 动态规划算法的迭代实现
要实现:每个子问题只计算一次
则需要:
1、明确迭代过程,要从最小的子问题算起
2、考虑计算顺序
3、保留结果,存储的数据结构
4、如何找到解
迭代的顺序,从最小的子问题,n=1起
举例子
依次计算r=2,3,4…8
我的分析
java代码实现
//矩阵链相乘
//问题描述:设A1,A2,...,An为矩阵序列,Ai为Pi-1*Pi阶矩阵,i=1,2,...,n.试确定矩阵的乘法顺序,使得元素相乘的总次数最少。
//输入:数组P【p0,p1...pn】 其中p0,p1,...,pn为n个矩阵的行数与列数
//输出:矩阵链乘法加括号的位置
public void MatrixChain(int[] P,int n) {
int[][] m = new int[n+1][n+1];
int[][] s = new int[n+1][n+1];
//遍历所有的矩阵链长度
for(int r = 2; r <= n; r++) {
//i为矩阵链左边界
for(int i = 0; i <= n - r; i++) {
//i为矩阵链左边界
int j = i+r;
m[i][j] = m[i][i+1] + m[i+1][j] + P[i] * P[i+1] * P[j]; //k=i+1时
s[i][j] = i+1; //记录划分位置k
for(int k = i+2; k <= j - 1; k++) {
int temp = m[i][k] + m[k][j] + P[i] * P[j] * P[k];
if(temp < m[i][j]) {
m[i][j] = temp;
s[i][j] = k;
}
}
}
}
//追踪解
getMatrixChain(P, m, s, 0, n);
}
private void getMatrixChain(int[] P, int[][] m, int[][] s, int i, int j) {
System.out.println("m[" + i + "][" + j + "]最少乘法次数为:" + m[i][j]);
int k = s[i][j];
if(j - i < 2) {
return;
}
getMatrixChain(P, m, s, i, k);
getMatrixChain(P, m, s, k, j);
}