動態規劃解決矩陣連乘
問題描述:
給定個矩陣 ,其中 和 和可以相乘的,即: 的行數等於 的列數,如何。確定矩陣連乘的計算次序,使得安照此次序計算該矩陣連乘所需要的數乘次數最少。
兩個矩陣相乘,它的數乘次數等於前一個矩陣的行數乘以後一個矩陣的列數,當這些矩陣相乘的次序不同時,它的數乘次數的有很大差異的。如下例題:
例:現有四個矩陣 ,他們的維數分別是:,則連乘 共有五種相乘的次序,如:
- ,數乘次數爲 16000
- ,數乘次數爲 36000
- ,數乘次數爲 34500
- ,數乘次數爲 10500
- ,數乘次數爲 87500
我們可以看到,連乘的次序不同,它的數乘次數大區別也是很大的,最少的 10500 次,最多的,87500 次,相差 8 倍之多。所以,尋找一個矩陣連乘的最優分隔方式就顯得尤爲重要。
解題思路:
這個問題可以使用枚舉法進行解決,枚舉每一個分隔方式的數乘次數,找到一個最小的即可,但是這樣的算法的是時間複雜度就會很大,並不是一個合格的算法,爲此,我們可以通過動態規劃來解決該問題。
將矩陣連乘 簡記爲 。
1. 😉設定一個二維數組 ,表示當前矩陣的連乘次數。比如: 表示矩陣從 連乘到 的數乘值。
2. 😉再設定一個二維數組 ,表示當前矩陣連乘時的具有最優解的分隔位置。比如 表示從矩陣從 連乘到 的數乘值最小時的分隔位置,可以理解爲加括號的位置。
給出遞歸關係:
核心算法詳解:
- 先將m數組的對角線初始化爲0,因爲 表示只有一個矩陣,那它的數乘次數當然是0了。
- 依次計算從第二個矩陣連乘到最後一個矩陣的最優解情況。
- 依次在 個分隔位置中依次檢測最優分隔點。
- 對於每個分隔點,變換一次分隔位置,再進行逐一測試,如果有更優的分隔點,就替換掉當前的分隔點。
矩陣連乘實例:
連乘矩陣 :
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;
}