矩阵连乘

Problem 1104 最优矩阵连乘积

Accepted: 29    Total Submit: 44
Time Limit: 1000ms    Memony Limit: 32768KB

Description

在科学计算中经常要计算矩阵的乘积。矩阵A和B可乘的条件是矩阵A的列数等于矩阵B的行数。若A是一个p×q的矩阵,B是一个q×r的矩阵,则其乘积C=AB是一个p×r的矩阵。其标准计算公式为:

由该公式知计算C=AB总共需要pqr次的数乘。
 

为了说明在计算矩阵连乘积时加括号方式对整个计算量的影响,我们来看一个计算3个矩阵{A1,A2,A3}的连乘积的例子。设这3个矩阵的维数分别为10×100,100×5和5×50。若按第一种加括号方式((A1A2)A3)来计算,总共需要10×100×5+10×5×50=7500次的数乘。若按第二种加括号方式(A1(A2A3))来计算,则需要的数乘次数为100×5×50+10×100×50=75000。第二种加括号方式的计算量是第一种加括号方式的计算量的10倍。由此可见,在计算矩阵连乘积时,加括号方式,即计算次序对计算量有很大影响。

于是,人们自然会提出矩阵连乘积的最优计算次序问题,即对于给定的相继n个矩阵{A1,A2,…,An}(其中Ai的维数为pi-1×pi ,i=1,2,…,n),如何确定计算矩阵连乘积A1A2…An的一个计算次序(完全加括号方式),使得依此次序计算矩阵连乘积需要的数乘次数最少。

Input

有若干种案例,每种两行,第一行是一个非负整数n表示矩阵的个数,n=0表示结束。接着有n行,每行两个正整数,表示矩阵的维数。

Ouput

对应输出最小的乘法次数。

Sample Input

310 100100 55 500

Sample Output7500

ABCD四个矩阵连乘

1、(A(BCD))——>(A(B(CD))),(A((BC)D));
2、((AB)(CD))——>NULL;
3、((ABC)D)——>((A(BC)D)),(((AB)C)D);
对于上面四个矩阵来说,枚举方法是:
1、括号加在A和B之间,矩阵链被分为(A)和(BCD);
2、括号加在B和C之间,矩阵链被分为(AB)和(CD);
3、括号加在C和D之间,矩阵链被分为(ABC)和(D);
在第一步中分出的(A)已经不能在加括号了,所以结束;
而(BCD)继续按照上面的步奏把括号依次加在B和C、C和D之间,其他情况相同。
加括号的过程是递归的。

备忘录法优化


上图为递归枚举过程,小方块内的1:4代表第1个矩阵至第4个矩阵的完全加括号方式

可以看到黄色方块中有很多重复计算,所以利用备忘录来保存计算结果,在每次进行计算前,
先查表,看是否计算过,避免重复计算。

#include<iostream>
#include<string.h>
using namespace std;
#define N 50
#define INF 9999999999
long long m[N][N];
int s[N][N],p[N];
int main()
{
	int n;
	while(cin>>n&&n)
	{
		int i,j,k;
		int x[N];
		for(i=0;i<2*n;++i)
			cin>>x[i];
		p[0]=x[0];
		//p[1]=x[1];
		j=1;
		for(i=1;i<2*n;i=i+2)
			p[j++]=x[i];
		memset(m,0,sizeof(m));
		memset(s,0,sizeof(s));
		int l;
		for(l=2;l<=n;++l)
		{
			for(i=1;i<=n-l+1;++i)
			{
				j=i+l-1;
				m[i][j]=INF;
				for(k=i;k<=j-1;++k)
				{
					long long q;
					q=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
					if(q<m[i][j])
					{
						m[i][j]=q;
						s[i][j]=k;
					}
				}
			}
		}
		cout<<m[1][n]<<endl;
	}
	return 0;
}

递归方法:
#include<iostream>
#include<string.h>
using namespace std;
#define N 50
#define INF 99999999
int m[N][N],s[N][N];
int p[N];
int n;
int matrix(int m[N][N],int p[N],int i,int j)
{
	if(i==j)
		m[i][j]=0;
	if(m[i][j]<INF)
		return m[i][j];
	
	else
	{
		for(int k=i;k<j;++k)
		{
			int q=matrix(m,p,i,k)+matrix(m,p,k+1,j)+p[i-1]*p[k]*p[j];
				if(q<m[i][j])
					{
						m[i][j]=q;
						s[i][j]=k;
					}
		}
		return m[i][j];
	}
}
int main()
{
	
	while(cin>>n&&n)
	{
		int i,j;
		int x[N];
		for(i=0;i<2*n;++i)
			cin>>x[i];
		p[0]=x[0];
		
		j=1;
		for(i=1;i<2*n;i=i+2)
			p[j++]=x[i];
		//for(i=0;i<=n;++i)
		//	cin>>p[i];
		memset(m,125,sizeof(m));
		
		memset(s,0,sizeof( s));
		matrix(m,p,1,n);
		cout<<m[1][n]<<endl;
	}
}

上面是备忘录法,下面是无备忘录法:
#include<iostream>
#include<string.h>
using namespace std;
#define N 50
#define INF 99999999
int m[N][N],s[N][N];
int p[N];
int n;
int matrix(int p[N],int i,int j)
{
	if(i==j)
		return 0;
	m[i][j]=INF;
	//else
	{
		for(int k=i;k<j;++k)
		{
			int q=matrix(p,i,k)+matrix(p,k+1,j)+p[i-1]*p[k]*p[j];
				if(q<m[i][j])
					{
						m[i][j]=q;
						s[i][j]=k;
					}
		}
		return m[i][j];
	}
}
int main()
{
	
	while(cin>>n&&n)
	{
		int i,j;
		int x[N];
		for(i=0;i<2*n;++i)
			cin>>x[i];
		p[0]=x[0];
		
		j=1;
		for(i=1;i<2*n;i=i+2)
			p[j++]=x[i];
		//for(i=0;i<=n;++i)
		//	cin>>p[i];
		memset(m,0,sizeof(m));
		memset(s,0,sizeof( s));
		matrix(p,1,n);
		cout<<m[1][n]<<endl;
	}
}

动态规划法


以矩阵链ABCD为例
按照矩阵链长度递增计算最优值
矩阵链长度为1时,分别计算出矩阵链A、B、C、D的最优值
矩阵链长度为2时,分别计算出矩阵链AB、BC、CD的最优值
矩阵链长度为3时,分别计算出矩阵链ABC、BCD的最优值
矩阵链长度为4时,计算出矩阵链ABCD的最优值

动归方程:




k为矩阵链断开的位置
d数组存放矩阵链计算的最优值,d[i][j]是以第i个矩阵为首,第j个矩阵为尾的矩阵链的最优值,i > 0
m数组内存放矩阵链的行列信息,m[i-1]和m[i]分别为第i个矩阵的行和列(i = 1、2、3...)

下面是加括号输出:
#include<iostream>
#include<string.h>
using namespace std;
#define N 50
#define INF 99999999
int m[N][N],s[N][N];
void print(int s[N][N],int i,int j)
{
	if(i==j)
		cout<<"A"<<i;
	else
	{
		cout<<"(";
		print(s,i,s[i][j]);
		print(s,s[i][j]+1,j);
		cout<<")";
	}
}
int main()
{
	int n;
	while(cin>>n&&n)
	{
		memset(m,0,sizeof(m));
		memset(s,0,sizeof(s));
		int p[N];
		int i,j,k,l;
		for(i=0;i<=n;++i)
			cin>>p[i];
		for(l=2;l<=n;++l)
		{
			for(i=1;i<=n-l+1;++i)
			{
				j=i+l-1;
				m[i][j]=INF;
				for(k=i;k<=j-1;++k)
				{
					int q=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
					if(q<m[i][j])
					{
						m[i][j]=q;
						s[i][j]=k;
					}
				}
			}
		}
		print(s,1,n);
		cout<<endl;
		cout<<m[1][n]<<endl;
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
矩阵连乘算法是一种用于计算多个矩阵相乘的方法,它可以通过动态规划的方式来降低计算的时间复杂度。在C语言中,可以使用二维数组来表示矩阵,并通过循环和递归来实现算法。 以下是一个简单的矩阵连乘算法的C语言实现示例: ```c #include <stdio.h> #include <limits.h> // 定义矩阵的最大数量 #define MAX_MATRICES 10 // 矩阵结构体 typedef struct { int rows; int cols; } Matrix; // 计算矩阵连乘的最小代价 int matrixChainOrder(Matrix matrices[], int n) { int dp[MAX_MATRICES][MAX_MATRICES]; // 初始化dp数组 for (int i = 1; i <= n; i++) { dp[i][i] = 0; } // 计算最小代价 for (int len = 2; len <= n; len++) { for (int i = 1; i <= n - len + 1; i++) { int j = i + len - 1; dp[i][j] = INT_MAX; for (int k = i; k < j; k++) { int cost = dp[i][k] + dp[k+1][j] + matrices[i].rows * matrices[k].cols * matrices[j].cols; if (cost < dp[i][j]) { dp[i][j] = cost; } } } } return dp[n]; } int main() { // 定义矩阵数组 Matrix matrices[MAX_MATRICES]; // 输入矩阵的行数和列数 int n; printf("请输入矩阵的数量:"); scanf("%d", &n); printf("请输入每个矩阵的行数和列数:\n"); for (int i = 1; i <= n; i++) { printf("矩阵%d:", i); scanf("%d %d", &matrices[i].rows, &matrices[i].cols); } // 计算最小代价 int minCost = matrixChainOrder(matrices, n); printf("矩阵连乘的最小代价为:%d\n", minCost); return 0; } ``` 这段代码中,我们首先定义了一个`Matrix`结构体来表示矩阵的行数和列数。然后,我们使用一个二维数组`dp`来存储计算过程中的最小代价。在`matrixChainOrder`函数中,我们使用动态规划的思想来计算最小代价。最后,在`main`函数中,我们输入矩阵的数量和每个矩阵的行数和列数,并调用`matrixChainOrder`函数来计算最小代价。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值