题目描述
用加括号的方式给出最优的矩阵相乘方案
输入
多组数据输入
第一行一个整数 n n n,表示矩阵链的长度(1<=n<=300)
接下来一行 n + 1 n+1 n+1个数表示这些矩阵的行数和列数
输出
对于每组数据,输出两行,第一行为计算次数,第二行为计算方案,用加括号的方式给出最优的矩阵相乘方案
如果不幸最优方案不唯一,选择优先计算左边的矩阵
输入样例
3
10 30 5 60
3
10 20 5 4
输出样例
4500
((A1A2)A3)
1200
((A1A2)A3)
Hint
在第二组样例中,选择((A1A2)A3)时,结果为10×20×5+10×5×4=1200
选择A1(A2A3)时,结果为20×5×4 + 10×20×4 = 1200
这时选择第一种,优先计算左边的!
思路分析
对于Ai到Aj矩阵链,假设我们知道应该从Ak后面截开,则相乘次数为Ai到Ak的次数加上A(k+1)到Aj的次数加上合并次数Ai行数Ak列数Aj列数
那么让k从Ai到A(j-1)遍历,找到使相乘次数最小的k即可
动态规划填表法:设置一个dp[i][j]存储矩阵Ai到Aj相乘的最小次数
i==j时,一个矩阵不需要相乘,次数为0
i!=j时,遍历循环控制变量l=j-i等于1、2、……、n-1的所有情况,需要用的时候查表即可获得子串的相乘次数,然后做出选择
(j-i从小到大遍历,需要子串相乘次数时dp中已经存在)
设置一个s[i][j]存储k表示Ai到Aj矩阵连乘应该从k出断开,即Ai到Ak、A(k+1)到Aj
AC代码
#include<stdio.h>
#define maxnum 100000000
int a[310];
int dp[310][310]={0},s[310][310];
void chain(int a[],int n);
void paint(int i,int j);
int main()
{
int i,n;
while((scanf("%d",&n))!=EOF)
{
for(i=0;i<=n;i++)
scanf("%d",&a[i]); //矩阵Ai的行列为A[i-1][i]
chain(a,n);
printf("%d\n",dp[1][n]);
paint(1,n);
printf("\n");
}
}
void chain(int a[],int n)
{
int i,j,l,k,temp;
for(l=1;l<=n-1;l++)
{
for(i=1;i<=n-l;i++)
{
j=i+l;
dp[i][j]=maxnum;
for(k=i;k<=j-1;k++)
{
temp=dp[i][k]+dp[k+1][j]+a[i-1]*a[k]*a[j];
if(temp<=dp[i][j])
{//这里的等于体现了左边优先
dp[i][j]=temp;
s[i][j]=k;
}
}
}
}
}
void paint(int i,int j)
{
if(i==j) printf("A%d",i);
else
{
printf("(");
paint(i,s[i][j]);
paint(s[i][j]+1,j);
printf(")");
}
}