零崎的朋友很多Ⅲ
题目描述
零崎有很多朋友,其中有一个叫jhljx。
jhljx大家很熟悉了,他数学不好也是出了名的,大家都懂。
现在jhljx遇到了矩阵乘法,他当时就懵了。数都数不清的他,矩阵乘法怎么可能会算的清楚呢?虽然零崎觉得还不如让你们来算,不过好歹也要给jhljx个面子,给她留下一个证明自己数学实力的机会。为了减小jhljx的计算量,让他赶快算出不正确答案来(估计他算上几遍都不一定能出一个正确答案),零崎请你们帮助jhljx。
输入
多组输入数据。
每组数据以N开始,表示矩阵链的长度。接下来一行N+1个数表示矩阵的行/列数。
1<=N<=300
输出
对于每组样例,输出一行最少运算次数的方案,每两个矩阵相乘都用“()”括起来,详见样例
如果存在多种解决方案,最终输出结果选取先计算左边的矩阵,详见Hint
输入样例
3
10 30 5 60
3
10 20 5 4
输出样例
((A1A2)A3)
((A1A2)A3)
Hint
对于输入的第二组数据,
如果计算顺序为((A1A2)A3),结果为10×20×5 + 10×5×4= 1200,
如果计算顺序为A1(A2A3), 结果为20×5×4 + 10×20×4 = 1200
那么输出结果选取第一个
本题分析:
这道题涉及到矩阵链乘的问题。
首先分析矩阵链乘。在矩阵链乘问题中,实际上并没有把矩阵相乘,目的是确定一个具有最小代价的矩阵相乘顺序。找出这样一个结合顺序使得相乘的代价最低。
那么可以得出状态转移方程:
m[i,j] =min{m[i,k]+m[k+1,j]+pi-1pkpj} (i≤k<j)
上述方程可以解释为:从第i个矩阵到第j个矩阵的最小代价,等于从第i个矩阵到第k个矩阵的最小代价加上从第k+1个矩阵到第j个矩阵的最小代价,再加上这三个矩阵之间连接的代价。
那么我们就可以在分析的基础上写下代码了。(其实这个代码是参考自某学长的...)
本题的另一个关键点在于输出的问题。这里采用递归的方法,如果i==j,那么就相当于是输出这个矩阵本身;否则的话就需要递归进行加括号处理。
还需要注意的地方是:最终输出的结果选取先计算左边的矩阵。也就是说需要在判断的时候需要m[i][j] >= temp,这里如果不取等于号,我也不知道会出现啥情况,但是感觉上就是因为要找到左边的一个边界值......?嗯对。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
#define maxn 330
int m[maxn][maxn];//记录从i矩阵到j矩阵的最优值
int rightKey[maxn][maxn];//用来找到正确的划分点
int p[maxn];//用来存储矩阵的行数或者列数
int n;
void printAnswer(int i, int j)
{
if(i == j)
printf("A%d", i);
else
{
printf("(");
printAnswer(i, rightKey[i][j]);
printAnswer(rightKey[i][j] + 1, j);
printf(")");
}
}
int main(){
while(scanf("%d", &n) == 1)
{
for(int i = 0; i <= n; i++)
{
scanf("%d", &p[i]);
}
for(int i = 0; i <= n; i++)
m[i][i] = 0;
for(int len = 2 ; len <= n; len++)//从第一个矩阵开始,到第n个矩阵结束
{
for(int i = 1; i <= n - len + 1; i++)
{
int j = i + len - 1;
m[i][j] = 0x3f3f3f3f;
for(int k = i; k < j; k++)//找到i和j之间的那一个最优的划分点k
{
int temp = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
//cout<<temp<<endl;
if(m[i][j] >= temp)
{
m[i][j] = temp;
rightKey[i][j] = k;
}
}
//cout<<rightKey[i][j]<<endl;
}
}
printAnswer(1, n);
printf("\n");
}
return 0;
}