动态规划
来源:互联网 发布:mp3音量调节软件 编辑:程序博客网 时间:2024/06/07 12:33
分治方法将问题划分为互不相交的子问题,递归的求解子问题,再将它们组合起来。
而动态规划应用与子问题重叠的情况(不同子问题具有公共的子子问题)来求解最优化啊问题。
设计动态规划算法的4个步骤:
1、刻画一个最优解的结构特征
2、递归定义最优解的值
3、计算最优解的值,通常采用自底向上的方法
4、利用计算出的信息构造一个最优解
最优子结构性质:问题的最优解有相关子问题的最优解组合而成,而这些子问题可以独立求解。
动态规划和分治策略相似,不同的是,它针对的问题所分解出的小问题数量很多且很多是重复的。
钢条切割问题
CUT-ROD(p,n) if n == 0 return 0 q=负无穷 for i = 1 to n q=max(q,p[i]+CUT-ROD(p,n-i)) return q
时间复杂度:
T(n)=1+T(0)+T(1)+...+T(n-1)
T(n)/T(n-1)=(1+T(0)+T(1)+...+T(n-1))/(1+T(0)+T(1)+...+T(n-2))=1+T(n-1)/(1+T(0)+T(1)+...+T(n-2))=2
所以上述算法时间复杂度为2的n次幂。
上面只用了分治策略,这个算法的性能是很差的T(n)=2n,在子问题的求解中很多都是重复的。
动态规划付出额外的内存空间来节省计算时间,是典型的时空权衡的例子。可能将一个指数时间的解转化为一个多项式时间的解。
自顶向下备忘录法:
MEM-CUT-ROD let r[0..n] be a new array for i = 0 to n r[i]=负无穷 return MEM-CUT-ROD-AUX(p,n,r) MEM-CUT-ROD-AUX(p,n,r) if r[n]>=0 return r[n] if n==0 q=0 else q=负无穷 for i=1 to n q=max(q,p[i]+MEM-CUT-ROD-AUX(p,n-i,r)) r[n]=q return q自底向上法:
BOTTOM-UP-CUT-ROD(p,n) let r[0..n] be a new array r[0]=0 for j=1 to n q=负无穷 for i=1 to j q=max(q,p[i]+r[j-i]) r[j]=q return r[n]这两种方法的时间复杂度都是O(n*n).
下面的伪代码还保留了切割长度
EXTEND-BOTTOM-UP-CUT-ROD(p,n) let r[0..n] and s[0..n] be new arrays r[0]=0 for j = 1 to n q=负无穷 for i =1 to j if q < p[i]+r[j-i] q=p[i]+r[j-i] s[j]=i r[j]=q return r and s PRINT-CUT-ROD-SOLUTION(p,n) (r,s)=EXTEND-BOTTOM-UP-CUT-ROD(p,n) while n >0 print s[n] n=n-s[n]
使用动态规划方法求解的最优化问题应该具备两个要素:最优子结构和子问题重叠.
矩阵链乘法
n个矩阵相乘,A1A2...An,试着找到一种相乘顺序,使得整个相乘的过程中所做的标量乘法次数最少,即相乘的代价最小。
问题分析:
记矩阵Ai的行列分别为Pi-1和Pi,那么A1*A2所做的乘法的次数为P0*P1*P2
A1A2...An的最小相乘代价为f(1,n),Ai...Aj的最小相乘代价为f(i,j)
则 f(1,n) = min{f(1,k)+f(k+1,n)+P0*Pk*Pn} k=1...n-1
f(i,i)=0 f(i,i+1)=Pi-1*Pi*Pi+1
So 可以由此产生递归式
Recursive_Matrix_Multiply(p, i, j){ if(i==j)return 0;f[i][j] = max_int;for(k=i;k<j;k++){q=Recursive_Matrix_Multiply(p,i,k)+Recursive_Matrix_Multiply(p,k+1,j)+p[i-1]*p[k]*p[j];if(q<f[i][j]){f[i][j]=q;s[i][j]=k;}}return f[i][j];}其中s[i][j]记录的是Ai...Aj相乘的最小代价的分割点,即先计算Ai...Ak,再计算Ak+1...Aj,然后再将结果相乘,Ai...Aj的相乘代价最小
递归的解法看起来思路清晰,但时间复杂度太高
若记n个矩阵的时间复杂度为T(n),则T(n)=∑k=1..n-1{T(k) +T(n-k) + c}, 可以推断T(n)>=2n
对递归算计的改进,可以引入备忘录,采用自顶向下的策略,维护一个记录了子问题的表,控制结构像递归算法。完整程序如下所示:
int memoized_matrix_chain(int *p,int m[N+1][N+1],int s[N+1][N+1]){ int i,j; for(i=1;i<=N;++i){ for(j=1;j<=N;++j){ m[i][j] = MAXVALUE; } } return lookup_chain(p,1,N,m,s);} int lookup_chain(int *p,int i,int j,int m[N+1][N+1],int s[N+1][N+1]){ if(m[i][j] < MAXVALUE) return m[i][j]; //直接返回,相当于查表 if(i == j) m[i][j] = 0; else { int k; for(k=i;k<j;++k) { int temp = lookup_chain(p,i,k,m,s)+lookup_chain(p,k+1,j,m,s) + p[i-1]*p[k]*p[j]; //通过递归的形式计算,只计算一次,第二次查表得到 if(temp < m[i][j]) { m[i][j] = temp; s[i][j] = k; } } } return m[i][j];}虽然给出了递归解的过程,但是在实现的时候不采用递归实现,而是借助辅助空间,使用自底向上的表格进行实现。设矩阵Ai的维数为pi-1pi,i=1,2.....n。输入序列为:p=<p0,p1,...pn>,length[p] = n+1。使用m[n][n]保存m[i,j]的代价,s[n][n]保存计算m[i,j]时取得最优代价处k的值,最后可以用s中的记录构造一个最优解。书中给出了计算过程的伪代码,摘录如下:
MAXTRIX_CHAIN_ORDER(p) n = length[p]-1; for i=1 to n do m[i][i] = 0; for t = 2 to n //t is the chain length do for i=1 to n-t+1 j=i+t-1; m[i][j] = MAXLIMIT; for k=i to j-1 q = m[i][k] + m[k+1][i] + qi-1qkqj; if q < m[i][j] then m[i][j] = q; s[i][j] = k; return m and s;
构造一个最优解
已经计算出来最小代价,并保存了相关的记录信息。因此只需对s表格进行递归调用展开既可以得到一个最优解。书中给出了伪代码,摘录如下:
PRINT_OPTIMAL_PARENS(s,i,j) if i== j then print "Ai" else print "("; PRINT_OPTIMAL_PARENS(s,i,s[i][j]); PRINT_OPTIMAL_PARENS(s,s[i][j]+1,j); print")";
- 动态规划!!!动态规划!!!
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- 动态规划
- Java千百问_08JDK详解(009)_java程序如何运行
- Fragment 使用解析
- github常用命令
- MFC中List box的用法
- 礼物[Codevs1321]
- 动态规划
- 在Visual Studio 2010/2012/2013/2015上使用C#开发Android/IOS安装包和操作步骤
- 某P保护之调戏权限清0的学习。
- linux驱动一 设备号的分配和管理
- 判别模型(Discriminative model)和生成模型(Generative model)
- shell脚本设置守护进程
- Android总结--SQLite
- Sublime Text3注册码(可用)
- phpstrom2016.1.2运行项目设置