矩阵链乘法问题

问题描述(详见算法导论P197-P198):

已知:给定n个矩阵构成的一个矩阵链(A1, A2, ..., An),矩阵Ai的维数为pi-1×pi

求:决定该矩阵链的乘法结合顺序(即加括号),使得矩阵链乘法的运行时间最短


几个前提概念

  • 矩阵链乘法的运行时间将以标量乘法(单行×单列)的次数来衡量
  • A是p×q矩阵,B是q×r矩阵,则A×B的运行时间为pqr
  • 矩阵乘法满足结合律


首先判断是否具有最优子结构

假设Ai...Aj的矩阵链乘法的最优解是在Ak与Ak+1之间分开的,即(Ai...Ak)(Ak+1...Aj),则子序列也必须是矩阵链乘法的最优解

由此可知,问题的最优解包含子问题的最优解,满足最优子结构


然后给出递归表达式

  • m[i, j]为矩阵链Ai...Aj的乘法的最短运行时间,m[1, n]即为问题所求的最优解的值
  • s[i, j]为运行时间为m[i, j]时的k值,此函数用于递归构造最优解
递归表达式如下


其中矩阵Ai的维数为pi-1 x pi


然后进行自底向上的求解

矩阵m是一个上三角矩阵(不需要考虑i>j的情况),且对角线上的元素值均为0

由上面的递归表达式知,m[i, j]的值只取决于m[i, k]和m[k+1, j](k≥i且k<j),因此自底向上的求解过程实际是按子序列的长度递增来进行的


最后构造最优解

在推导过程中记录s[i, j]的值,用于递归构造最优解(即矩阵链的圆括号添加顺序)


代码:

int ** dp(int * p, int n){  //获得m[i, j]的值
	int ** m = (int **)malloc(n*sizeof(int *));
	int ** s = (int **)malloc(n*sizeof(int *));
	for(int i = 0; i < n; i++){
		m[i] = (int *)malloc(n*sizeof(int));
		memset(m[i], 0, n);
		s[i] = (int *)malloc(n*sizeof(int));
		memset(s[i], 0, n);
	}

	for(int l = 2; l <= n; l++){  //子序列长度由2到n递增
		for(int i = 0; i <= n-l; i++){  //注意i的上限
			int j = i+l-1;
			m[i][j] = INT_MAX;
			for(int k = i; k < j; k++){
				if(m[i][j] > m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1]){  //根据递归表达式计算m[i][j]
					m[i][j] = m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1];
					s[i][j] = k;  //记录s[i][j]
				}
			}
		}
	}
	
	printf("m: %d\n", m[0][n-1]);

	return s;
}

void print_parens(int ** s, int i, int j, int n){  //递归地构造最优解
	int k = s[i][j];
	if(i != j){
		if(!(i == 0 && j == n-1))
			printf("(");
		print_parens(s, i, k, n);
		print_parens(s, k+1, j, n);
		if(!(i == 0 && j == n-1))
			printf(")");
	}
	else{
		printf("A%d", i);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值