超詳細的動態規劃解決矩陣連乘

動態規劃解決矩陣連乘

在這裏插入圖片描述
問題描述:
  給定NN個矩陣 {A1,A2,A3An}\{A_1,A_2,A_3……A_n\},其中 AiA_{i}Ai+1A_{i+1} 和可以相乘的,即: AiA_i 的行數等於 Ai+1A_{i+1} 的列數,如何。確定矩陣連乘的計算次序,使得安照此次序計算該矩陣連乘所需要的數乘次數最少。
  兩個矩陣相乘,它的數乘次數等於前一個矩陣的行數乘以後一個矩陣的列數,當這些矩陣相乘的次序不同時,它的數乘次數的有很大差異的。如下例題:

例:現有四個矩陣 ABCDA,B,C,D,他們的維數分別是:A=5010B=1040C=4030D=305A=50*10,B=10*40,C=40*30,D=30*5,則連乘 ABCDA,B,C,D共有五種相乘的次序,如:

  1. (A((BC)D))(A((BC)D)),數乘次數爲 16000
  2. ((AB)(CD))((AB)(CD)),數乘次數爲 36000
  3. ((A(BC))D)((A(BC))D),數乘次數爲 34500
  4. (A(B(CD)))(A(B(CD))),數乘次數爲 10500
  5. (((AB)C)D)(((AB)C)D),數乘次數爲 87500

我們可以看到,連乘的次序不同,它的數乘次數大區別也是很大的,最少的 10500 次,最多的,87500 次,相差 8 倍之多。所以,尋找一個矩陣連乘的最優分隔方式就顯得尤爲重要。


解題思路:
  這個問題可以使用枚舉法進行解決,枚舉每一個分隔方式的數乘次數,找到一個最小的即可,但是這樣的算法的是時間複雜度就會很大,並不是一個合格的算法,爲此,我們可以通過動態規劃來解決該問題。
  將矩陣連乘 AiAi+1AjA_i*A_{i+1}……A_j 簡記爲 A[i:j]A[i:j]
  1. 😉設定一個二維數組 m[N][N]m[N][N],表示當前矩陣的連乘次數。比如: m[i][j]m[i][j] 表示矩陣從 AiA_i 連乘到 AjA_j 的數乘值。
  2. 😉再設定一個二維數組 S[N][N]S[N][N],表示當前矩陣連乘時的具有最優解的分隔位置。比如 s[i][j]s[i][j] 表示從矩陣從 AiA_i 連乘到 AjA_j 的數乘值最小時的分隔位置,可以理解爲加括號的位置。

給出遞歸關係:
m[i][j]={0,i==jmin{m[i][k]+m[k+1][j]+Ai1AkAj},i<j m[i][j] = \begin{cases} 0,& \text{i==j} \\ min\{m[i][k]+m[k+1][j]+A_{i-1}*A_k*A_j\}, & \text{i<j} \end{cases}

核心算法詳解:

  1. 先將m數組的對角線初始化爲0,因爲 m[i][i]m[i][i] 表示只有一個矩陣,那它的數乘次數當然是0了。
  2. 依次計算從第二個矩陣連乘到最後一個矩陣的最優解情況。
    1. 依次在 r1r-1 個分隔位置中依次檢測最優分隔點。
    2. 對於每個分隔點,變換一次分隔位置,再進行逐一測試,如果有更優的分隔點,就替換掉當前的分隔點。

矩陣連乘實例:

連乘矩陣 :{A1,A2,A3,A4,A5,A6}\{A_1,A_2,A_3,A_4,A_5,A_6\}
在這裏插入圖片描述
m數組:
在這裏插入圖片描述
s數組:
在這裏插入圖片描述

#include<iostream>
using namespace std;
const int N = 100;
int A[N];//矩陣規模
int m[N][N];//最優解
int s[N][N];
void MatrixChain(int n)
{
	int r, i, j, k;
	for (i = 0; i <= n; i++)//初始化對角線
	{
		m[i][i] = 0;
	}
	for (r = 2; r <= n; r++)//r個矩陣連乘
	{
		for (i = 1; i <= n - r + 1; i++)//r個矩陣的r-1個空隙中依次測試最優點
		{
			j = i + r - 1;
			m[i][j] = m[i][i]+m[i + 1][j] + A[i - 1] * A[i] * A[j];
			s[i][j] = i;
			for (k = i + 1; k < j; k++)//變換分隔位置,逐一測試
			{
				int t = m[i][k] + m[k + 1][j] + A[i - 1] * A[k] * A[j];
				if (t < m[i][j])//如果變換後的位置更優,則替換原來的分隔方法。
				{
					m[i][j] = t;
					s[i][j] = k;
				}
			}
		}
	}
}
void print(int i, int j)
{
	if (i == j)
	{
		cout << "A[" << i << "]";
		return;
	}
	cout << "(";
	print(i, s[i][j]);
	print(s[i][j] + 1, j);//遞歸1到s[1][j]
	cout << ")";
}
int main()
{
	int n;//n個矩陣
	cin >> n;
	int i, j;
	for (i = 0; i <= n; i++)
	{
		cin >> A[i];
	}
	MatrixChain(n);
	cout << "最佳添加括號的方式爲:";
	print(1, n);
	cout << "\n最小計算量的值爲:" << m[1][n] << endl;
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章