最优矩阵链乘次序

推荐参考: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;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值