最優矩陣鏈乘
[題目]
一個n*m的矩陣由n行m列共n*m排列而成。兩個矩陣A和B可以相乘當且僅當A的列數等於B的行數。一個n*m的矩陣乘m*p的矩陣,運算量爲n*m*p。
矩陣乘法不滿足分配律,但滿足結合律。因此A*B*C既可以按順序(A*B)C也可以按A(B*C)來進行。假設A、B、C分別是2*3、3*4、4*5的,則(A*B)C運算量是2*3*4+2*4*5=64,A(B*C)的運算量是3*4*5*2*3*5=90.顯然第一種順序節省運算量。
給出n個矩陣組成的序列,設計一種方法把他們依次乘起來,使得總的運算量儘量小。假設第i個矩陣A[i]是P[i-1]*P[i]的。
輸入
3
2 3
3 4
4 5
輸出
64
[分析]
矩陣鏈乘可以看成一個表達式,無論加多少括號,一定存在最後一次乘法,而最後一次乘號的左右兩邊的子表達式一定存在最優解(運算量最小),也就是最優子結構。狀態轉移方程如下:
f(i,j)=min{f(i,k)+f(k+1,j)+pi-1pkpj};
其中f(i,j)表示第i個矩陣到第j個矩陣的運算量,k爲中間乘號的位置,p保存矩陣的行和列,第i個矩陣的行爲pi-1,列爲pi(這裏用到了一個小技巧,由於輸入是每一個矩陣的行和列,且前一個矩陣的列和後一個的行相同,在保存時重合讀入)。
由於算法複雜度爲O(n3),採用記憶化搜索優化。
[代碼]
#include<bits/stdc++.h>
using namespace std;
const int maxsize=2000+5;
const int INF=1000000000;
int d[maxsize][maxsize];
int p[maxsize];
int dp(int i,int j)
{
if(d[i][j]>0) return d[i][j];
if(i==j) return 0;
d[i][j]=INF;
for(int k=i;k<=j;k++)
{
d[i][j]=min(d[i][j],dp(i,k)+dp(k+1,j)+p[i-1]*p[k]*p[j]);
}
return d[i][j];
}
int main()
{
freopen("input.txt","r",stdin);
int n,T;
cin>>T;
for(int j=0;j<T;j++)
{
memset(d,0,sizeof(d));
cin>>n;
for (int i=1;i<=n; i++)
{
cin>>p[i-1]>>p[i];
}
if(n==1)
cout<<p[0]*p[1]<<endl;
else
cout<<dp(1,n)<<endl;
}
return 0;
}