題目:n個矩陣連乘,求最少的乘法運算次數以及結合方式ios
假設矩陣A爲r1*r2,矩陣B爲r2*r3,因此M=A*B=r1*r2*r3。當有多個矩陣相乘的時候,矩陣以不一樣的方式結合的時候其運算次數是不一樣的。算法
例如:M=M1 * M2 * M3 * M4spa
[5*20] [20*50] [50*1] [1*100]code
((M1*M2)*M3)*M4=5000+250+500=5750遞歸
而M1*(M2*(M3*M4))=5000+100000+10000=115000input
若是按照(M1*(M2*M3)*M4)=1000+100+500=1600io
上面的例子說明根據不一樣的結合順序,運算的次數是不一樣的。如今咱們就是要求經過哪一種順序所進行的乘法運算次數最少,而且求出運算最少的次數是多少。class
算法思想:stream
記Mi*Mi+1*Mi+2*...*Mj爲mij,矩陣的大小爲:M1爲r1*r2,M2爲r2*r3,M3爲r3*r3,M4爲r4*r5。r1,r2,r3,r4,r5分別爲5,20,50,1,100.變量
(1)首先計算矩陣相乘個數爲2的3種狀況。
m12=M1*M2=r1*r2*r3=5000,m23=r2*rr*r4,m34=r3*r4*r5=1000,m34=5000
(2)計算相乘矩陣個數爲3的2種狀況
m13=M1*M2*M3,目標是求出m13的乘法運算次數最小值。m13=min{M1*m23+r1*r2*r4,m12*M3+r1*r3*r4}=min{1100,5250}=m12*M3+r1*r3*r4=1100
同理 m24=M2*M3*M4=min{M2*m34+r2*r3*r,m23*M4+r2*r4*r5}=min{10500,3000}=3000;
(3)最後計算相乘矩陣個數爲4的最終結果:
m14=min(m13*M4+r1*r4*r5,m12*m34+r1*r3*r4,M1*m24+r1*r2*r5)={3000+10000 ,5000+5000,+25000,1100+500}=1600
算法以下
#include <iostream> using namespace std; int r[100],com[100][100]; int course(int i,int j) { int u,t; if(i==j) return 0; if(i==j-1) { com[i][j]=i; return r[i]*r[i+1]*r[i+2]; } else { u=course(i,i)+course(i+1,j)+r[i]*r[i+1]*r[j+1]; com[i][j]=i; for(int k=i;k<j;k++) { t=course(i,k)+course(k+1,j)+r[i]*r[k+1]*r[j+1]; if(t<u) { u=t; com[i][j]=k; } } return u; } } int main() { int n; printf("input n:"); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&r[i]); printf("min=%d\n",course(1,n-1)); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) printf("%d ",com[i][j]); printf("\n"); } return 0; }
遞歸調用的過程以下:
1-2,2-3,3-4等子問題都被遞歸調用了2次,子問題存在重複。能夠採用備忘錄方法解決該問題,設置一個全局變量m[i][j[,存儲已經計算過的course(i,j)的值,下次調用的時候直接使用。
非遞歸算法
非遞歸的算法計算過程和算法主要思想裏面的一致。運算的矩陣的個數從少到多慢慢增長。
for(int i=1;i<n;i++) { m[i][i]=0; m[i][i+1]=r[i]*r[i+1]*r[i+2]; com[i][i+1]=i; } m[n][n]=0; //動態規劃 for(int s=2;s<n;s++) for(int i=1;i<=n-s+1;i++) { j=i+s; m[i][j]=m[i][i]+m[i+1][j]+r[i]*r[i+1]*r[j+1]; com[i][j]=i; for(int k=i+1;k<j;k++) { t=m[i][k]+m[k+1][j]+r[i]*r[k+1]*r[j+1]; if(t<m[i][j]) m[i][j]=t; com[i][j]=k; } }