UVA - 348 Optimal Array Multiplication Sequence (最优矩阵连乘)

本文深入探讨了最优矩阵链乘法问题的解决方法,通过动态规划技术找到最小乘法次数的矩阵乘法顺序。文章详细介绍了状态转移方程,并提供了一个C++实现示例。
题目大意:给你一些矩阵,你要写一个程式来决定该如何相乘的顺序,使得用到乘法的次数会最少。

思路:最优矩阵链乘问题,典型的动态规划题目。
该问题的子问题为“把Ai,Ai+1,……,Aj乘起来需要多少次乘法”,如果用dp(i,j)表示这个问题的子问题的值,
则状态转移方程:dp(i,j) = min{dp(i,k) + dp(k+1,j) + row(i)*col(k)*col(j)}
row代表当前矩阵的行,col代表当前矩阵的列。

总结:由于dp[i][j]的初始条件没有写好,结果wa了很多次,没考虑全,以为dp[i][j],初始化为0就好了。


#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 15;
struct Matrix {
	int row,col;
}mat[N];
int dp[N][N], path[N][N];
void print_path(int l,int r) {
	if(l == r) {
		printf("A%d",l+1);
		return ;
	}else {
		printf("(");
		print_path(l,path[l][r]);
		printf(" x ");
		print_path(path[l][r]+1,r);
		printf(")");
	}
}
int main() {
	int n , cas = 1;
	while(scanf("%d",&n) != EOF && n) {
		for(int i = 0; i < n; i++) {
			scanf("%d%d", &mat[i].row, &mat[i].col);
		}
		memset(dp,0,sizeof(dp));
		memset(path,0,sizeof(path));
		for(int len = 1; len < n; len++) { //枚举长度
			for(int i = 0; i < n - len; i++) { //枚举起点
				int j = i + len;
				dp[i][j] = dp[i][i] + dp[i+1][j] + mat[i].row * mat[i].col * mat[j].col;
				path[i][j] = i;
				for(int k = i+1; k < j; k++) { //枚举中断点
					int tmp = dp[i][k] + dp[k+1][j] + mat[i].row * mat[k].col * mat[j].col;
					if(tmp < dp[i][j]) {
						dp[i][j] = tmp;
						path[i][j] = k;
					}
				}
			}
		}
		printf("Case %d: ",cas++);
		print_path(0,n-1);
		printf("\n");
	}
	return 0;
}

### 最优二叉查找树 最优二叉查找树(Optimal Binary Search Tree, OBST)是一种特殊的二叉查找树,其目标是在给定一组键及其访问频率的情况下构建一棵使得平均查找成本最小化的二叉查找树。对于每一个可能的查询序列,OBST 都能提供最短路径长度来定位所需数据项。 为了实现这一优化过程,通常采用动态规划的方法解决该问题。通过定义一个二维数组 `cost[i][j]` 来存储从第 i 到 j 的关键字构成的最佳 BST 所需的成本,并逐步填充此表直至找到全局解。最终得到的结果不仅包含了最低预期代价的信息,还隐含了如何构造这棵树的具体指导[^1]。 ```python def optimal_bst(keys, freq): n = len(keys) cost = [[0 for _ in range(n)] for _ in range(n)] root = [[None for _ in range(n)] for _ in range(n)] for length in range(1, n + 1): for i in range(n - length + 1): j = i + length - 1 if i >= j: cost[i][j] = 0 continue min_cost = float('inf') for r in range(i, j + 1): c = ((sum(freq[i:j+1]) + (cost[i][r-1] if r != i else 0) + (cost[r+1][j] if r != j else 0))) if c < min_cost: min_cost = c root[i][j] = r cost[i][j] = min_cost return cost[0][-1], build_tree(root, keys) def build_tree(matrix, keys): def helper(i, j): if matrix[i][j]: k = matrix[i][j] node = TreeNode(keys[k]) if k > i and matrix[i][k-1]: node.left = helper(i, k-1) if k < j and matrix[k+1][j]: node.right = helper(k+1, j) return node return helper(0, len(keys)-1) class TreeNode(object): """Definition of a binary tree node.""" def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right ``` ### 矩阵连乘 矩阵相乘问题是另一个经典的动态规划案例,旨在寻找一种方式将多个矩阵顺序相乘以使总的标量运算次数最少。假设存在一系列大小分别为 p_0×p_1, p_1×p_2,...,p_{n−1}×p_n 的矩阵 A_1,A_2,…A_n ,则可以通过调整括号的位置改变计算次序从而影响总的操作数。 解决方案同样依赖于动态规划技术,创建 m 和 s 数组分别记录子问题的最优值以及对应的分割位置。遍历所有可能的划分点并选取能使当前区间内的累积开销达到最小的那个作为最佳方案。最后利用辅助函数重构完整的加括号表达式表示整个链条中最有效的组合形式。 ```python import sys def matrix_chain_order(p): n = len(p) - 1 m = [[0]*n for _ in range(n)] s = [[0]*n for _ in range(n)] for l in range(2, n+1): for i in range(n-l+1): j = i+l-1 m[i][j] = sys.maxsize for k in range(i,j): q = m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1] if q<m[i][j]: m[i][j]=q s[i][j]=k return m,s def print_optimal_parens(s,i,j): if i==j: print(f"A{i}",end="") else: print("(", end='') print_optimal_parens(s,i,s[i][j]) print_optimal_parens(s,s[i][j]+1,j) print(")", end='') if __name__=='__main__': dims=[30,35,15,5,10,20,25] _,solutions=matrix_chain_order(dims) print_optimal_parens(solutions,0,len(dims)-2) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值