矩阵连乘的动态规划算法

矩阵链乘问题

输入:
共两行
第一行 N ( 1<=N<=100 ),代表矩阵个数。
第二行有 N+1 个数,分别为 A1 、 A2 ...... An+1 ( 1<=Ak<=2000 ), Ak 和 Ak+1 代表第 k 个矩阵是个 Ak X Ak+1 形的。
输出:
共两行
第一行 M ,为最优代价。注:测试用例中 M 值保证小于 2^31
第二行为最优顺序。如 (A1((A2A3)A4)) ,最外层也加括号。
注意:测试用例已经保证了输出结果唯一,所以没有AAA的情况.

input
6
30 35 15 5 10 20 25
output
15125
((A1(A2A3))((A4A5)A6))

思路分析:

1.分析最优解的结构
设计算A[1:n](第1项到第n项连成的最优解),那么它的子链A[1:k]和A[k+1:n]的次序也是最优的。
若不然,不妨设有一个计算比A[1:k]的次序计算量更少,则用它替换原来的A[1:k],那么A[1:n]的计算量
将比最优次序所需的计算量更少,与题设矛盾。因此最优子结构成立。

2.确定状态及状态方程
设问题即为状态m[i][j]表示从第i项连乘到第j项的最优计算次数。
m[i][j] = 0 (i==j)
  = min{m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j], 其中i<=k<j}  (i<j)

3.计算最优值
初始化 i:1->n  m[i][i] = 0;
每次沿斜对角线向右上方计算,斜对角线标号变化:2->n

4.构造最优解
根据定义,递归输出最优解。
输出结果为(A[1:k]的最优次序,A[k+1:n]的最优次序)的格式
依据此形式编写递归函数进行输出。
注:在计算最优解的过程中注意记录求最优计算顺序的过程
下边为循环由底向上的dp

#include <cstdio>
#include <iostream>
#define N 105
using namespace std;

int n;
int p[N], m[N][N], s[N][N];

void printTrace(int i, int j)//根据最优子结构确定
{
	if (i == j)
	{
		printf("A%d", i);
		return;
	}
	printf("(");
	printTrace(i, s[i][j]);
	printTrace(s[i][j] + 1, j);
	printf(")");
}

int main()
{
	scanf("%d", &n);
	for (int i = 0; i <= n; i++)
		scanf("%d", &p[i]);
	
	if (n == 1)//特判
	{
		printf("0\n");
		printf("(A1)\n");
		return 0;
	}
	
	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++)
			{
				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;
				}
			}
		}
	}

	printf("%d\n", m[1][n]);
	printTrace(1, n);
	printf("\n");

	return 0;
}
下边是采用递归和记忆化搜索的方式dp
#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 105
using namespace std;

int n;
int s[N][N], m[N][N], p[N];

void init()
{
	for (int i = 1; i <= n; i++)
		for (int j = i; j <= n; j++)
			m[i][j] = 0;
}

int matrixchain(int i, int j)
{
	if (m[i][j] > 0)	return m[i][j];
	if (i == j)	return 0;
	int u = matrixchain(i, i) + matrixchain(i + 1, j) + p[i - 1] * p[i] * p[j];
	s[i][j] = i;
	for (int k = i + 1; k < j; k++)
	{
		int t = matrixchain(i, k) + matrixchain(k + 1, j) + p[i - 1] * p[k] * p[j];
		if (t < u)
		{
			u = t;
			s[i][j] = k;
		}
	}
	return m[i][j] = u;
}

void printTrace(int i, int j)
{
	if (i == j)
	{
		printf("A%d", i);
		return;
	}
	printf("(");
	printTrace(i, s[i][j]);
	printTrace(s[i][j] + 1, j);
	printf(")");
}

int main()
{	
	scanf("%d", &n);
	for (int i = 0; i <= n; i++)
		scanf("%d", &p[i]);

	if (n == 1)//特判
	{
		printf("0\n");
		printf("(A1)\n");
		return 0;
	}

	init();

	int ans = matrixchain(1, n);
	printf("%d\n", ans);
	printTrace(1, n);
	printf("\n");

	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值