动态规划矩阵求和

一.矩阵链事例

在这里插入图片描述

矩阵链问题主要涉及的时在多个矩阵相乘,如何通过相乘的顺序来减少程序运行。
这次分析过程按照动态规划的三个基本条件来逐步解答:

1、寻找最优子结构:假设我们已经找到父矩阵链最优解,当我们划分到最后一步时都是两个子矩阵链(分别被括号包围)相乘,如(A1A2A3A4)(A5A6A7),此时显然外括号为父矩阵的最优括号划分。继续往下划分,((A1A2A3)A4)(A5(A6A7)),则两个子矩阵链的划分括号必须也为他们本身的最优括号划分。因为如果子矩阵链的划分括号不是他们本身的最优括号划分时,两个子矩阵链就有另一种最优括号划分,如(A1(A2A3A4))(A5(A6A7))。那么就与我们的假设相悖。((A1A2A3)A4)(A5(A6A7))不是我们父矩阵最优解。所以拥有最优划分的父矩阵的子矩阵链显然也拥有最优划分。

2、子问题重叠:看到步骤一是不是感觉这个问题可以使用递归来解决?显然可以。算法导论给出如下递归公式:

在这里插入图片描述
但是我们发现递归公式中会有很多重复计算的子结构。由于k是未知,所以我们需要循环来找出最小的,但是,当k=i……j时k每对应一个值就得继续往下递归,此时又得循环一遍k,在其中就会有许多子问题重复计算了。比如我们有这两个括号划分:

(A1(A2A3A4))(A5(A6A7))和(A1(A2A3A4))((A5A6)A7),此时前面子链括号划分是相同的而后面子链划分不同。如果我们使用15.12进行递归就会重复计算(A1(A2A3A4))。这正是动态规划解决的问题。

3、自底向上方法:我们前面使用的循环使用的是自上向下的方法。而动态规划使用的是自底向上的方法。也就是从最初向最后推理。如果我们简单的一直划分而不做比较的话,我们会发现划分的分支越来越多。而动态规划不同,它是在划分中把一些明显代价大的,可以比较的pk掉。从而减少分析次数,而且自底向上还避免了子问题的重复计算。而想要从底部开始就要求我们每次的划分都是最优划分,从而被下一次划分最优划分包含在所有下一次划分方法之内,这也是我们为什么首先要证明步骤一。

// 2.12.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"


int _tmain(int argc, _TCHAR* argv[])
{
	return 0;
}

#include"stdafx.h"
#include<iostream>
using namespace std;

#define N 7  //N为7,实际表示有6个矩阵
/*
*矩阵链构造函数:构造m[][]和s[][]
*m中存储的值是计算出来的最小乘法次数,比如m[1][5]就是A1A2A3A4A5的最小乘法次数
*s中存储的是获取最小乘法次数时的断链点,s[1][5]对应的就是如何拆分A1A2A3A4A5,
*比如S[1][5]=3可表示:(A1A2A3)(A4A5),当然内部断链还会继续划分A1A2A3
*/
int MatrixChain(int *p, int n, int m[][N], int s[][N]){
	for (int i = 1; i <= n; i++){     //矩阵链中只有一个矩阵时,次数为0,注意m[0][X]时未使用的
		m[i][i] = 0;
	}
	for (int r = 2; r <= n; r++){    //矩阵链长度,从长度为2开始
		for (int i = 1; i <= n - r + 1; i++){    //根据链长度,控制链最大的可起始点
			int j = i + (r - 1);  //矩阵链的末尾矩阵,注意r-1,因为矩阵链为2时,实际是往右+1
			m[i][j] = m[i][i] + m[i + 1][j] + p[i - 1] * p[i] * p[j];   //先设置最好的划分方法就是直接右边开刀,后续改正,也可合并到下面的for循环中
			s[i][j] = i;
			for (int k = i + 1; k < j; k++){   //这里面将断链点从i+1开始,可以断链的点直到j-1为止
				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;
				}

			}

		}
	}
	return m[1][n];
}
/*
*追踪函数:根据输入的i,j限定需要获取的矩阵链的始末位置,s存储断链点
*/
void Traceback(int i, int j, int s[][N]){
	if (i == j)       //回归条件
	{
		cout << "A" << i;
	}
	else       //按照最佳断点一分为二,接着继续递归
	{
		cout << "(";
		Traceback(i, s[i][j], s);
		Traceback(s[i][j] + 1, j, s);
		cout << ")";
	}
}
int main(){
	int p[N] = { 30, 35, 15, 5, 10, 20, 25 };
	int m[N][N], s[N][N];

	int yy = MatrixChain(p, N - 1, m, s);//N-1因为只有六个矩阵

	for (int i = 1; i < N; i++){
		for (int j = 0; j < N; j++){
			if (m[i][j] == -858993460)
				cout << "     ";
			else
				cout << m[i][j] << "     ";
		}
		cout << endl;
	}

	for (int i = 1; i <= N; i++){
		for (int j = 0; j <= N; j++){
			if (s[i][j] == -858993460)
				cout << "     ";
			else
				cout << s[i][j] << "     ";
		}
		cout << endl;
	}


	cout << "这个数是:" << yy << endl;
	Traceback(1, 6, s);
	system("pause");
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值