动态规划算法与分治法类似,其基本思想也是将带求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。但分治法中子问题之间是相互独立的,子问题的不同划分不会影响最终的解,而动态规划中子问题往往不是相互独立的,每个子问题之间相互影响,即不同的子问题划分会最终导致最终解的不同。
动态规划算法的算法优化在于通过将每个子问题求解的值记录,由于计算多个彼此关联 的子问题的时候会重复计算相同问题,故通过查找记录的值以避免大量重复的计算,这就是动态规划的基本思想,故动态规划也称作蛮力法或者穷举法。通常可以按以下步骤设计动态规划算法:
1.找出最优解的性质,并刻画其结构特征;
2.递归地定义最优值;
3.以自底向上的方式记录并计算出最优值;
4.根据记录值,构造最优解;
动态规划算法的基本要素:
1.最优子结构:
即规模相同的子问题中,不同的划分有着不同的解,且存在最优解
2.重叠子问题;
在细化划分子问题的过程中存在相互影响的性质,即每次产生的子问题并不总是新问题,即上一级子问题的解影响着当前子问题的解。
矩阵连乘
由于矩阵乘法满足结合律,估计算矩阵连乘积可以有许多不同的计算次序 ,这种计算次序可以通过加括号的方式来确定。
void Algorithm::traceback(int s[7][7],int i,int j)
{
if(i==j) return;
traceback(s,i,s[i][j]);
traceback(s,s[i][j]+1,j);
cout<<"(A"<<i<<s[i][j]<<")*("<<"A"<<s[i][j]+1<<j<<")"<<endl;
}
void Algorithm::matrixChain(int p[],int m[7][7],int s[7][7],int n)
{
for(int i=0;i<n;i++) m[i][i]=0;
for(int j=0;j<n;j++) m[0][j]=0;
for(int r=1;r<n;r++)
for(int i=1;i<n-r;i++)
{
int j=i+r;
m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];//m[i][i]+m[j][j]和m[i+1][j]的区别
s[i][j]=i;
for(int k=i+1;k<j;k++)
{
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;
}
}
}
}
最长公共子序列
最长公共子序列具有以下最优子结构性质
设序列X{x1,x2…xm},Y{y1,y2,y3…ym},他们的最长公共子序列Z={z1,z2,…zk}
1.若xm=yn,则zk=xm=yn,且Zk-1是Xm-1和Yn-1的最长公共子序列
2.xm!=yn,且zk!=xm,则Z是Xm-1和Y的最长公共子序列
3.xm!=xn,且zk!=yn,则Z是X和Yn-1最长公共子序列
void Algorithm::Ics(int i,int j,char x[],int b[8][7])
{
if(i==0||j==0) return;//表示某一行或者某一列的元素全部删除
if(b[i][j]==1)
{
Ics(i-1,j-1,x,b);
cout<<x[i]<<endl;
}
else
{
if(b[i][j]==2)
Ics(i-1,j,x,b);
else if(b[i][j]==3)
Ics(i,j-1,x,b);
}
}
//b[i][j]通过不同的数字记录不同情况下的最大子序列,在构造子序列的时候通过不同的记录判定删除那个集合中的最后一个元素
int Algorithm::IcsLength(char x[],char y[],int b[8][7])
{
int c[8][7];
for(int i=0;i<8;i++)
for(int j=0;j<7;j++)
{
c[i][j]=0;
}
for(i=1;i<8;i++)
for(int j=1;j<7;j++)
{
if(x[i]==y[j])//这个地方存在BUG,由于数组的起始位置不同,导致无法全部比较数组元素
{
c[i][j]=c[i-1][j-1]+1;
b[i][j]=1;
}
else
{
if(c[i-1][j]>=c[i][j-1])
{
c[i][j]=c[i-1][j];
b[i][j]=2;//子序列为前一行的最长子序列,故删除地i行的一个元素。
}
else
{
c[i][j]=c[i][j-1];
b[i][j]=3;//子序列为前一列的最长子序列,股删除行j列的一个元素。
}
}
}
return c[8][7];
}