一、设计动态规划算法的步骤
(1).找出最优解的性质
(2).动态规划方程
(3).自底向上计算最优值
二、矩阵连乘积问题
计算三个矩阵ABC的成绩,由于矩阵乘法的性质,不同计算顺序导致的乘法运算量可能相差悬殊。即(AB)C 和A(BC)的运算量差距很大。
A:行数*列数 50*10 B:10*40 C:40*30
乘法运算次数:
(AB)C=(50*10*40)+50*40*30 =80000
A(BC)=50*10*30+(10*40*30)=27000
第二种计算方法的运算量是明显小于第一种的
三、分析最优解的结构
使用A[i,j]代表Ai,Ai+1...Aj的连乘积,做一个断点k,i<=k<j,k将矩阵的连乘积分割为两部分,Ai,Ai+1...Ak,Ak+1...Aj。以此,对乘积Ai,Ai+1...Aj的完全加括号的代价就是计算Ai,Ai+1...Ak,Ak+1...Aj的代价之和,再加上两者相乘的代价。
即:(Ai,Ai+1...Ak)+(Ak+1...Aj)+pi-1pkpj
Ai的维数为 Ai-1*temp Ai-1的原因是p[i]从0开始存储
//p[i]为行数,temp为列数
for(i=0;i<=n-1;i++)
scanf("%d%d",&p[i],&temp);
若Ai,Ai+1...Aj的一个最优完全加括号方式是以k为断点,分割矩阵,则Ai,Ai+1...Ak的子链必须是它的一个最优完全加括号方式。反之,若存在Ai,Ai+1...Ak的一个代价更小的最优完全加括号方式,则它替换到Ai,Ai+1...Aj中则会产生另一个最优解。这样一来,任何最优解都包含子问题的最优解。
四、建立递归关系
A[i,j]代表Ai,Ai+1...Aj的连乘积,m[i][j]代表A[i][j]的最少次数。
A[1,n]代表A1,A2...An的连乘积,m[1][n]代表A[1,n]的最优解。
i,起始位置,j,终止位置。
当i=j,代表矩阵链只包含一个矩阵,A[1,1]代表矩阵链中只包含矩阵A1。
则m[i][i]=0.
i<j,在Ai,Ai+1...Aj中加一个断点k,i<=k<j,则m[i][j]等于子乘积A[i,k]和A[k+1,j]的和在加上两个矩阵相乘的乘法运算次数(代价),乘法运算次数就是m*n 矩阵A * n*p 矩阵B = m *p 矩阵C
图片来源:
https://www.cnblogs.com/beatrice7/p/4149639.html
定义数组s[i][j]保存k的值。
#include<bits/stdc++.h>
using namespace std;
#define NUM 51
int p[NUM];//行列数p[i-1]行数,p[i]为列数
int m[NUM][NUM];//保存最优解
int s[NUM][NUM];//保存断点k的值
int LookupChain(int i,int j)
{
if(m[i][j]>0)return m[i][j];//m[i][j]存了子问题的运算结果
if(i==j)return 0;//只含一个矩阵的情况。
int u = LookupChain(i,i)+LookupChain(i+1,j)+p[i-1]*p[i]*p[j];//递推公式,以i为第一个断点
s[i][j] = i;
for(int k=i+1;k<j;k++)//从i后找不同断点的情况,保存最小值为最优解m[i][j]
{
int t = LookupChain(i,k)+LookupChain(k+1,j)+p[i-1]*p[k]*p[j];
if(t<u){u=t;s[i][j]=k;}
}
m[i][j]=u;
return u;
}
矩阵 | A1 | A2 | A3 | A4 | A5 | A6 |
行列数 | 50*10 | 10*40 | 40*30 | 30*5 | 5*20 | 20*15 |
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
值 | 50 | 10 | 40 | 30 | 5 | 20 | 15 |
五、构建最优解
(A[i,k])(A[k+1,j]),令s[i][j]=k
则A[1,n]的最优加括号方式为(A([1,s[1][n]))(A(s[1][n]+1,n))
void TraceBack(int i,int j)
{
if(i==j)printf("A%d",i);
else
{
printf("(");
TraceBack(i,s[i][j]);
TraceBack(s[i][j]+1,j);
printf(")");
}
}
完整代码为:
#include<bits/stdc++.h>
using namespace std;
#define NUM 51
int p[NUM];
int m[NUM][NUM];
int s[NUM][NUM];
int LookupChain(int i,int j)
{
if(m[i][j]>0)return m[i][j];
if(i==j)return 0;
int u = LookupChain(i,i)+LookupChain(i+1,j)+p[i-1]*p[i]*p[j];
s[i][j] = i;
for(int k=i+1;k<j;k++)
{
int t = LookupChain(i,k)+LookupChain(k+1,j)+p[i-1]*p[k]*p[j];
if(t<u){u=t;s[i][j]=k;}
}
m[i][j]=u;
return u;
}
void TraceBack(int i,int j)
{
if(i==j)printf("A%d",i);
else
{
printf("(");
TraceBack(i,s[i][j]);
TraceBack(s[i][j]+1,j);
printf(")");
}
}
int main()
{
int n;
scanf("%d",&n);
int i,temp;
for(i=0;i<=n-1;i++)
scanf("%d%d",&p[i],&temp);
p[n]=temp;
memset(m,0,sizeof(m));
LookupChain(1,n);
printf("%d\n",m[1][n]);
TraceBack(1,n);
return 0;
}
/*
input:
6
50 10
10 40
40 30
30 5
5 20
20 15
output:
15750
((A1(A2(A3A4)))(A5A6))
*/
本博客资料、代码来源于清华大学出版社算法设计与分析,本博客仅用于个人学习,可能存在纰漏,敬请批评指正。