推荐参考:http://blog.csdn.net/wuxiushu/article/details/51245313
还有高文宇老师的课件,并画几个矩阵帮助分析。
代码:
/* r contains number of columns for each of the N matrices */
/* r[ 0 ] is the number of rows in matrix 1 */
/* Minimum number of multiplications is left in M[ 1 ][ N ] */
void OptMatrix( const long r[ ], int N, TwoDimArray M )
{ int i, k, Left, Right;
long ThisM;
for( Left = 1; Left <= N; Left++ ) M[ Left ][ Left ] = 0;///初始化自己到自己的最小链乘为0
for( k = 1; k < N; k++ ) /* k = Right - Left *确定L和R间的跨度
for( Left = 1; Left <= N - k; Left++ ) { /* For each position */ ///左边界线所能取的范围
Right = Left + k; M[ Left ][ Right ] = Infinity; ///因为求最小,所以开始初始化为无穷
for( L = Left; L < Right; L++ ) {///求区间LEFT-->RIGHT的最小链乘
ThisM = M[ Left ][ L ] + M[ L + 1 ][ Right ]///M[I][J]表示I到J的最小链乘次数,r[i-1]*r[i]为矩阵的维度
+ r[ Left - 1 ] * r[ L ] * r[ Right ];///这里描述为第一个矩阵的行乘第二个矩阵额列再乘第三个矩阵的列的出来的次数
if ( ThisM < M[ Left ][ Right ] ) /* Update min */
M[ Left ][ Right ] = ThisM;
} /* end for-L */
} /* end for-Left */
}
今天练了两题的这道题的应用,加深了对应的印象。
先是RQNOJ652。与普通的直接算矩阵的最优链乘稍微有一点不同的是这道题的矩阵是成环的。
思路:在上面的矩阵线性链乘的基础上变成环形的,只要把环型构造成连续的两段完全一样的n个矩阵,然后从1-n个位置开始找最小的矩阵链乘次数。(这个思路需牢记)
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=500;
int n;
long long times[maxn][maxn],thismin,ans;
struct matrix{
long long row,col;
};
matrix m[maxn];///m[l][r]:从l到r的最小矩阵链乘的次数
long long dp(int st,int ed){
//pair<int,int> p[maxn];
//int cnt=0;
for(int i=st;i<=ed;i++)
times[i][i]=0;
for(int k=1;k<=n-1;k++)///左右边界的跨度
for(int left=st;left<=ed-k;left++){
int right=left+k;
times[left][right]=inf;
for(int l=left;l<right;l++){
thismin=times[left][l]+times[l+1][right]+m[left].row*m[l].col*m[right].col;
if(thismin<times[left][right]){
times[left][right]=thismin;
//cout<<l<<" "<<left<<" "<<right<<endl;
}
}
}
return times[st][ed];
}
int main(){
while(scanf("%d",&n)!=EOF){
thismin=inf;
ans=inf;
for(int i=1;i<=n;i++){
scanf("%d%d",&m[i].row,&m[i].col);
m[i+n].row=m[i].row;
m[i+n].col=m[i].col;
}
for(int i=1;i<=n;i++)
ans=min(ans,dp(i,i+n-1));
printf("%lld\n",ans);
}
return 0;
}
另一题就是需要打印路径。对于遇到括号的路径打印,其实有点类似二叉树的中序遍历。(最优链乘子矩阵每次都被划分为左右两部分,这就像二叉树的左子树和右子树)所以需要每次记录子区间的最优划分的位置,然后类似于中序遍历那样进行输出。(同样需要牢记)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=500;
int n,s[maxn][maxn],casenum=0;
long long times[maxn][maxn],thismin,ans;
struct matrix{
long long row,col;
};
matrix m[maxn];///m[l][r]:从l到r的最小矩阵链乘的次数
long long dp(int st,int ed){
//pair<int,int> p[maxn];
//int cnt=0;
for(int i=st;i<=ed;i++)
times[i][i]=0;
for(int k=1;k<=n-1;k++)///左右边界的跨度
for(int left=st;left<=ed-k;left++){
int right=left+k;
times[left][right]=inf;
for(int l=left;l<right;l++){
thismin=times[left][l]+times[l+1][right]+m[left].row*m[l].col*m[right].col;
if(thismin<times[left][right]){
times[left][right]=thismin;
s[left][right]=l;
//cout<<l<<" "<<left<<" "<<right<<endl;
}
}
}
return times[st][ed];
}
int print(int st,int ed){
if(st==ed)
printf("A%d",st);
else{
printf("(");
print(st,s[st][ed]);
printf(" x ");
print(s[st][ed]+1,ed);
printf(")");
}
}
int main(){
while(scanf("%d",&n)!=EOF){
if(n==0)
break;
casenum++;
thismin=inf;
ans=inf;
for(int i=1;i<=n;i++){
scanf("%d%d",&m[i].row,&m[i].col);
}
dp(1,n);
printf("Case %d: ",casenum);
print(1,n);
printf("\n");
}
return 0;
}