动态规划应用举例之矩阵链乘法
矩阵链乘法问题
给定n个矩阵的链<A1,A2,...An>,矩阵Ai的规模为P(i-1)*Pi(1<=i<=n),求完全括号化方案,使得计算乘积A1A2...An所需标量乘法次数最少。
比如矩阵链<A1,A2,A3>相乘,不同的加括号方式会导致不同的计算代价。假定三个矩阵的规模为10*100、100*5、5*50。若按((A1A2)A3)计算,A1A2需要做10*100*5=5000次标量乘法(根据矩阵相乘代码得出所述计算方法),再与A3相乘又需要做10*5*50=2500次标量乘法,合起来共需7500次标量乘法。若按(A1(A2A3))的顺序,计算A2A3需要做100*5*50=25000次标量乘法,A1再与之相乘又需10*100*50=50000次标量乘法,合起来共需75000次标量乘法。
整体思路
为方便,用Aij表示AiA(i+1)...Aj乘积的结果矩阵。进行括号化,也就是在某个Ak和A(k+1)之间将矩阵链分开。所以,解决办法就是先计算矩阵A(i...k)的计算代价,加上矩阵A(k+1...j)的计算代价,再加上两者相乘的计算代价即为最小计算代价。
代码
MATRIX-CHAIN-ORDER(p)
for i=1 to n //长度为1的链的最小计算代价为0
m[i,j]=0
for l=2 to n //l代表链的长度,由小到大,因为链长的计算依赖于较短的链
for i=1 to n-l+1 //根据链的长度计算所要求的m[i,j]中的i,i的最大值要有限定,否则有可能超出给定的矩阵链(比如矩阵链是0
//A2...A6,假定链长为3,那么i最大为4,若i=5,则需要A7。)
j=i+l-1 //根据链的长度l和i的值计算j的值
m[i,j]=100000000 //先给定一个比所有计算结果都大的值
for k=i to j-1 //k为可能的矩阵链的分割点位置
q=m[i,k]+m[k+1,j]+P(i-1)P(k)P(j) //计算对应的代价
if q<m[i,j]
m[i,j]=q //矩阵Aij所需标量乘法次数的最小值
s[i,j]=k //最优括号方案的分割点位置k
return m and s
PRINT-OPTIMAL-PARENS(s,i,j)
if i==j
print "A";
else
print "("
PRINT-OPTIMAL-PARENS(s,i,s[i,j])
PRINT-OPTIMAL-PARENS(s,s[i,j]+1,j) print ")"
动态规划应用举例之最长公共子序列
最长公共子序列问题
给定两个序列X=<x1,x2,...,xm>和Y=<y1,y2,...,yn>,求X和Y长度最长的公共子序列。
伪代码
LCS-LENGTH(X,Y)
m=X.length
n=Y.length
for i=1 to m //若一个序列长度为0,那么LCS的长度为0
c[i,0]=0
for j=0 to n //同上
c[0,j]=0
for i=1 to m 由递归解计算LCS的长度
for j=1 to n
if xi==yi
c[i,j]=c[i-1,j-1]+1
b[i,j]="↖" //表示xi=yj是LCS的一个元素
else if c[i-1,j]>=c[i,j-1]
c[i,j]=c[i-1,j]
b[i,j]="↑"
else
c[i,j]=c[i,j-1]
b[i,j]="←"
return c and b
打印LCS
PRINT-LCS(b,X,i,j)
if i==0 or j==0
return 0
if b[i,j]=="↖"
PRINT-LCS(b,X,i-1,j-1)
print xi
else if b[i,j]=="↑"
PRINT-LCS(b,X,i-1,j)
else
PRINT-LCS(b,X,i,j-1)