给定n个矩阵{A1,A2,…An},其中Ai 与Ai+1 是可乘的,i = 1,2,3…n-1。考查这n 个矩阵的连乘积A1A2….An。
比如A1A2A3,可以有(A1A2)A3,A1(A2A3) 这两种方式。
若使用穷举搜索法,其复杂度是随着n的增长呈指数增长的,可以使用动态规划的时候,即求其最优子结构。设AiAi+1…Aj记作A[i : j],设A[i : j],1 <= i <= j <= n,所需要的最少数乘次数为m[i][j].则有:
m[i][j] = 0, i = j;
m[i][j] = min{m[i][k] + m[k+1][j] + pi - 1pkpj }, i < j .
s[][]用来记录最优解的位置,最后通过递归的方式,确定最后的计算次序。
首先要明确的是:所给出的矩阵必须是可以连乘的!
下面给出我们假设要计算的数据是,A1A2A3A4A5A6,其中A1:30*35,A2:35*15,A3:15*5,A4:5*10,A5:10*20,A6:20*25
所以我们的p数组的值为,p[] = {30,35,15,5,10,20,25};
要理解m,s的意义,以及在取最优解的时候的操作。可以在纸上模拟一下,还是比较好理解的。
#include
#include
#include
// m[i][j]:从第i个数组到第j个数组,求最优值数组,s[][]:最优断开位置的数组
//n 表示测试数组的个数
int matrixChan(int *p,int n, int **m, int **s)
{
// int n = strlen(p);
for(int i = 1; i <= n; i ++)
m[i][i] = 0;
for(int r = 2; r <= n; r ++) //r为连乘矩阵的个数
{
for(int i = 1; i <= n - r + 1; i ++)
{
int j = i + r - 1; // i 表示起始,j表示第r个
m[i][j] = m [i + 1][j] + p[i - 1]*p[i]*p[j];
s[i][j] = i;
for(int k = i + 1; k < j; k ++) //在第一个到第r个数组连乘,寻找最优的断开点
{
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;
}
}
}
}
return m[1][n]; // 返回第一个到第n个,即题目给出数据的答案
}
void traceback(int **s, int i, int j)
{
if(i == j)
return;
traceback(s, i, s[i][j]);
traceback(s, s[i][j] + 1, j);
printf("Multiply A %d,%d and A %d,%d\n", i, s[i][j] ,(s[i][j] + 1), j );
}
int main()
{
int p[6];
p[0] = 30;
p[1] = 35;
p[2] = 15;
p[3] = 5;
p[4] = 10;
p[5] = 20;
p[6] = 25;
//new出测试的数组。
int **m, **s;
int n = 7;
m = (int **)malloc(n*sizeof(int *));
s = (int **)malloc(n*sizeof(int *));
for (int i = 0; i < n; i++)
{
m[i] = (int *)malloc(n*sizeof(int));
s[i] = (int *)malloc(n*sizeof(int));
}
int ans = matrixChan(p,6, m, s);
printf("%d\n",ans);
traceback(s, 0, 6);
}
结果:
从最后的结果可以看出,最优的次数是15125,然后最优的计算次序是((A1(A2A3)) ((A4A5)A6)) .
Matrixchain的计算量取决于r,i,k的循环,为O(n3),空间复杂度为O(n2)。相比与穷举搜索更加高效。
不知道为啥,最近失眠超级严重的,特别是周六的晚上,于是乎,写完这篇已经快凌晨4点了,而我还是毫无睡意。
大三、、、、2015、11、01 03:45