动态规划——矩阵连乘的问题
《问题的引出》
看下面一个例子,计算三个矩阵连乘{A1,A2,A3};维数分别为10*100 , 100*5 , 5*50
按此顺序计算需要的次数((A1*A2)*A3):10X100X5+10X5X50=7500次
按此顺序计算需要的次数(A1*(A2*A3)):10X5X50+10X100X50=75000次
所以问题是:如何确定运算顺序,可以使计算量达到最小化。
枚举显然不可,如果枚举的话,相当于一个“完全加括号问题”,次数为卡特兰数,卡特兰数指数增长,必然不行。
《建立递归关系》
子问题状态的建模(很关键):令m[i][j]表示第i个矩阵至第j个矩阵这段的最优解。
显然如果i=j,则m[i][j]这段中就一个矩阵,需要计算的次数为0;
如果i>j,则m[i][j]=min{m[i][k]+m[k+1][j]+p[i-1]Xp[k]Xp[j]},其中k,在i与j之间游荡,所以i<=k 代码实现时需要注意的问题:计算顺序!!! 因为你要保证在计算m[i][j]查找m[i][k]和m[k+1][j]的时候,m[i][k]和m[k+1][j]已经计算出来了。 观察坐标的关系如图: 所以计算顺序如上右图:相应的计算顺序对应代码为13-15行 m[1][n]即为最终求解,最终的输出想为((A1(A2 A3))((A4 A5)A6))的形式,不过没有成功,待思考...
2
using
namespace
std;
3
const
int
MAX
=
100
;
4
//
p用来记录矩阵的行列,main函数中有说明
5
//
m[i][j]用来记录第i个矩阵至第j个矩阵的最优解
6
//
s[][]用来记录从哪里断开的才可得到该最优解
7
int
p[MAX
+
1
],m[MAX][MAX],s[MAX][MAX];
8
int
n;
//
矩阵个数
9
10
void
matrixChain(){
11
for
(
int
i
=
1
;i
<=
n;i
++
)m[i][i]
=
0
;
12
13
for
(
int
r
=
2
;r
<=
n;r
++
)
//
对角线循环
14
for
(
int
i
=
1
;i
<=
n
-
r
+
1
;i
++
){
//
行循环
15
int
j
=
r
+
i
-
1
;
//
列的控制
16
//
找m[i][j]的最小值,先初始化一下,令k=i
17
m[i][j]
=
m[i][i]
+
m[i
+
1
][j]
+
p[i
-
1
]
*
p[i]
*
p[j];
18
s[i][j]
=
i;
19
//
k从i+1到j-1循环找m[i][j]的最小值
20
for
(
int
k
=
i
+
1
;k
<
j;k
++
){
21
int
temp
=
m[i][k]
+
m[k
+
1
][j]
+
p[i
-
1
]
*
p[k]
*
p[j];
22
if
(temp
<
m[i][j]){
23
m[i][j]
=
temp;
24
//
s[][]用来记录在子序列i-j段中,在k位置处
25
//
断开能得到最优解
26
s[i][j]
=
k;
27
}
28
}
29
}
30
}
31
32
//
根据s[][]记录的各个子段的最优解,将其输出
33
void
traceback(
int
i,
int
j){
34
if
(i
==
j)
return
;
35
36
traceback(i,s[i][j]);
37
traceback(s[i][j]
+
1
,j);
38
cout
<<
"
Multiply A
"
<<
i
<<
"
,
"
<<
s[i][j]
<<
"
and A
"
<<
s[i][j]
+
1
<<
"
,
"
<<
j
<<
endl;
39
}
40
41
int
main(){
42
cin
>>
n;
43
for
(
int
i
=
0
;i
<=
n;i
++
)cin
>>
p[i];
44
//
测试数据可以设为六个矩阵分别为
45
//
A1[30*35],A2[35*15],A3[15*5],A4[5*10],A5[10*20],A6[20*25]
46
//
则p[0-6]={30,35,15,5,10,20,25}
47
//
输入:6 30 35 15 5 10 20 25
48
matrixChain();
49
50
traceback(
1
,n);
51
//
最终解值为m[1][n];
52
cout
<<
m[
1
][n]
<<
endl;
53
return
0
;
54
}