SHUOJ - 算法题1 矩阵连乘问题(区间dp)

链接:http://acmoj.shu.edu.cn/problem/24/

分析:设\(dp[i][j]\)为矩阵\(A[i:j]\)所需的最少乘法次数,则有dp方程:\(dp[i][j]=min\{dp[i][k]+dp[k+1][j]\}(i\leq k <j)\),当\(i=j\)时,\(dp[i][j]=0\).
则可以递归求解该问题,递归终止的条件是\(i=j\),自底向上地解决问题。枚举\([i,j)\)之间的每个k,并取其最小值。
因为该题需要输出解决方案,所以求解时,若\(dp[i][j] = dp[i][k]+dp[k+1][j]\),则用数组\(path[i][j]\)记录下这个k。
输出结果时,仍采用递归的方式,将一个括号内的视作一个子结构,递归终止的条件是区间中的元素仅有一个,此时打印单个矩阵。注意除了最外层的区间不用输出括号,加特判即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e2+5;
const int INF=0x3f3f3f3f;
typedef long long LL;
int a[maxn],b[maxn];
LL dp[maxn][maxn];
int path[maxn][maxn];
int n;
int dfs(int i,int j)
{
    LL &ans=dp[i][j];
    if(ans<INF) return ans;
    if(i==j) return ans=0;
    for(int k=i;k<j;k++){
        int tmp=dfs(i,k)+dfs(k+1,j)+a[i]*b[k]*b[j];     //递归求解
        if(tmp<ans){
            ans=tmp;                                    //不断更新最小值和其路径
            path[i][j]=k;                               
        }
    }
    return ans;
}

void print(int i,int j)
{
    if(i==j){                     //递归终止
        printf("A%d",i);          //输出独立矩阵
        return;
    }
    if(i>1||j<n) printf("(");     //最外层不要括号  
    print(i,path[i][j]);          //递归进入子区间
    print(path[i][j]+1,j);
    if(i>1||j<n) printf(")");    //最外层不要括号
}

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    int cas=0;
    while(scanf("%d",&n)==1){
        for(int i=1;i<=n+1;i++){
            scanf("%d",&a[i]);
            if(i>1) b[i-1] = a[i];      //后一个矩阵的行数等于前一个矩阵的列数
        }
        memset(dp,INF,sizeof(dp));      //初值置无穷大
        memset(path,0,sizeof(path));    //路径
        dp[1][n]=dfs(1,n);
        printf("Case %d\n",++cas);
        printf("%lld ",dp[1][n]);
        print(1,n);
        printf("\n");
    }
    return 0;
}

posted @ 2018-09-26 19:03  xiuwenL  阅读(667)  评论(0编辑  收藏  举报