矩陣鏈乘法問題
給定一個n個矩陣的序列
考慮矩陣鏈
如果按
如果按
爲了得到所需乘法次數最少的方案,需要計算所有種方案的代價。
對一個n個矩陣的鏈,令P(n) 表示可供選擇的括號化方案的數量。
完全括號化方案的矩陣乘積可以描述爲兩個完全括號化的部分相乘的形式,
k爲分割點,即第k個矩陣和第k+1個矩陣之間
可以看出括號化方案的數量與n呈指數關係
動態規劃
1.最優括號化方案的結構特徵
由於要求得矩陣鏈
2.遞歸求解方案
令
當
當
即:
3.計算最優代價
遞歸算法會多次遇到同一個子問題,與鋼鐵切割很類似,每一次高層的運算,都會調用底層結果,越是底層,被調用的次數越多。所以可以採用自底向上的方法,先對底層逐個求解,當上層需要調用底層時,底層已經被求解完畢。
用m[i][j]二維矩陣保存對應鏈
用s[i][j]二維矩陣保存對應鏈
void Matrix_Chain_Order(int p[],int n)
{
int i,j,L,k,q;
for (i=1;i<=n;i++) //先對單個矩陣的鏈,求解,即所有m[i][i] =0;
{
m[i][i]=0;
}
for(L=2;L<=n;L++) //從兩個矩陣鏈的長度開始,逐次增加矩陣鏈的長度
for(i=1;i<=n-L+1;i++) //在給定p[]中的矩陣鏈中,對所有種長度爲L的情況計算
{
j = i+L-1;
m[i][j] = -1;
for(k=i;k<=j-1;k++) //遍歷所有可能的劃分點k,計算出最優的劃分方案
{
q = m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];//計算劃分的代價
if ( q < m[i][j] || m[i][j] == -1)
{
m[i][j] = q; //最優的代價q保存在m[i][j]中
s[i][j] = k; //最優的劃分位置k保存在s[i][j]中
}
}
}
}
構造最優解
矩陣鏈
例程
/************************************************************************
CSDN 勿在浮沙築高臺
http://blog.csdn.net/luoshixian099
算法導論--動態規劃(矩陣鏈乘法)
2015年6月3日
************************************************************************/
#include <STDIO.H>
#include <STDLIB.H>
int m[7][7]={0};
int s[7][7]={0};
void Print_Optimal_Parens(int s[][7],int i,int j) //構造最優解
{
if ( i ==j)
{
printf("A%d",i);
}
else
{
printf("(");
Print_Optimal_Parens(s,i,s[i][j]);
Print_Optimal_Parens(s,s[i][j]+1,j);
printf(")");
}
}
void Matrix_Chain_Order(int p[],int n)
{
int i,j,L,k,q;
for (i=1;i<=n;i++) //先對單個矩陣的鏈,求解,即所有m[i][i] =0;
{
m[i][i]=0;
}
for(L=2;L<=n;L++) //從兩個矩陣鏈開始,逐次增加矩陣鏈的長度
for(i=1;i<=n-L+1;i++) //在給定p[]中的矩陣鏈中,對所有種長度爲L的情況計算
{
j = i+L-1;
m[i][j] = -1;
for(k=i;k<=j-1;k++) //遍歷所有可能的劃分點k,計算出最優的劃分方案,
{
q = m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
if ( q < m[i][j] || m[i][j] == -1)
{
m[i][j] = q; //最優的代價q保存在m[i][j]中
s[i][j] = k; //最優的劃分位置k保存在s[i][j]中
}
}
}
}
void main()
{
int p[]={30,35,15,5,10,20,25}; //矩陣的輸入
int length = sizeof(p)/sizeof(p[0])-1; //矩陣長度
int i,j;
Matrix_Chain_Order(p,length);
for(i =1;i<=6;i++)
{
for (j=1;j<=6;j++)
{
printf("%8d",m[i][j]);
}
printf("\n");
}
Print_Optimal_Parens(s,1,6);
printf("\n");
}