动态规划——矩阵连乘积问题(个人学习用)

一、设计动态规划算法的步骤

(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;
}
矩阵A1A2A3A4

A5

A6
行列数50*1010*4040*3030*55*2020*15
下标0123456
5010403052015

 

 五、构建最优解

(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))
*/

本博客资料、代码来源于清华大学出版社算法设计与分析,本博客仅用于个人学习,可能存在纰漏,敬请批评指正。

在C语言中计算矩阵的连乘并寻找最小值,通常需要先理解矩阵乘法的基本原理,然后设计算法来实现。矩阵乘法涉及将第一个矩阵的行与第二个矩阵的列对应元素相乘求和,得到的结果构成新的矩阵。如果有多组矩阵需要连乘,你需要按照正确的顺序依次进行。 为了找到最小值,你可以在每次乘法运算后比较当前结果和之前记录的最小值,并更新这个最小值。这里的关键点在于高效的内存管理和循环结构: ```c #include <stdio.h> #include <stdlib.h> // 矩阵乘法函数 int** matrix_multiply(int** A, int rowsA, int colsA, int** B, int rowsB, int colsB) { int* result = (int*)malloc(sizeof(int) * rowsA * colsB); if (!result) { printf("Memory allocation failed.\n"); return NULL; } for (int i = 0; i < rowsA; ++i) { for (int j = 0; j < colsB; ++j) { result[i * colsB + j] = 0; for (int k = 0; k < colsA; ++k) { result[i * colsB + j] += A[i][k] * B[k][j]; } } } int** M = (int**)malloc(sizeof(int*) * rowsA); for (int i = 0; i < rowsA; ++i) M[i] = result + i * colsB; // 最小值初始化为第一个矩阵的所有元素 int minValue = *result; return M; } // 主函数示例,可以添加遍历和比较最小值的部分 int main() { int** mat1 = ...; // 第一个矩阵 int** mat2 = ...; // 第二个矩阵 // 更多矩阵... int minProduct = INT_MAX; for (int i = 0; i < numMatrices; ++i) { int** temp = matrix_multiply(mat1, ...); // 计算连乘 // 比较最小值并更新 minProduct = temp[0][0] < minProduct ? temp[0][0] : minProduct; free(temp); // 释放内存 } printf("最小矩阵连乘: %d\n", minProduct); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值