题目的主要信息:
- 编写程序计算不同的计算顺序时矩阵乘法需要进行的乘法次数
- 计算顺序由字符串给出,A-Z的大写字母表示矩阵,括号决定运算顺序,每次运算都有括号
- 比如 ( ( A B ) C ) 或者 ( A ( B C ) )
- 进阶要求:时间复杂度,空间复杂度
方法一:递归
具体做法:
输入的运算规则中,字母与矩阵一一对应,如果没有括号,就是依次从左到右计算,然后结果新矩阵存在靠右的矩阵的位置,继续往右计算。加了括号之后,我们还是可以这样,只需要把括号中的问题当成子问题进入递归即可。
遍历运算法则字符串,如果是左括号我们进入括号以后开始遍历到右边,每次遇到一个左括号累增一个层数,遇到一个右括号递减一个层数,层数为0则最后的右括号刚好匹配了最左端的左括号,则两个括号之间就是子问题,可以送入函数递归,递归的结果我们返回括号计算后的矩阵,方便与其他部分再计算,我们用一个数组记录每一轮递归中计算的矩阵(事实上,数组中只会有一个矩阵),如果此时该数组为空,则递归的结果送入数组,否则与数组中的元素计算,并更新数组为计算后的矩阵。
如果遇到非括号,意味着遇到矩阵,按照上述说法要么加入空数组中,要么与数组中的矩阵计算后更新。
如果遇到右括号,则代表一个子问题结束,返回数组记录的矩阵,最终累加的次数就是需要计算的乘法量。
#include<iostream>
#include<stack>
#include<string>
#include<vector>
using namespace std;
pair<int, int> compute(string s, vector<pair<int, int>>& matrix, int& count){
vector<pair<int, int> > st; //记录要计算的矩阵
for(int i = 1; i < s.length();){
if(s[i] == '('){ //进入左括号
int layer = 0; //统计左括号层数
int j = i;
while(j <= s.length()){ //遍历到右边
if(s[j] == '(')
layer++; //遇到左括号,层数累加
else if(s[j] == ')'){
layer--; //遇到右括号层数递减
if(layer == 0) //直到层数为0
break;
}
j++;
}
auto mat = compute(s.substr(i, j - i + 1), matrix, count);//递归计算括号中的部分
i = j + 1;
if(st.empty()) //st为空,进入到st中
st.push_back(mat);
else{ //否则与st中元素计算,并更新结果矩阵的大小
count += st[0].first * st[0].second * mat.second;
st[0] = make_pair(st[0].first, mat.second);
}
}else if(s[i] == ')') //该子递归结束
return st[0];
else{ //非括号意味着是矩阵
if(st.empty()) //st为空,进入到st中
st.push_back(matrix[s[i] - 'A']);
else{ //否则与st中元素计算,并更新结果矩阵的大小
auto cur = matrix[s[i] - 'A'];
count += st[0].first * st[0].second * cur.second;
st[0] = make_pair(st[0].first, cur.second);
}
i++;
}
}
return matrix[0]; //没有实际作用,函数必要的返回语句
}
int main(){
int n;
while(cin >> n){
vector<pair<int, int> > matrix(n);
string rule;
for(int i = 0; i < n; i++) //输入n个矩阵的行列数
cin >> matrix[i].first >> matrix[i].second;
cin >> rule; //输入运算法则
stack<char> s; //记录代表矩阵的字符
int count = 0;
compute(rule, matrix, count);
cout << count << endl;
}
return 0;
}
复杂度分析:
- 时间复杂度:,相当于遍历计算个矩阵的乘法次数
- 空间复杂度:,递归栈最坏情况下为
方法二:栈
具体做法:
上述递归也可以用栈来代替。 我们用一个栈记录代表矩阵的字符,遍历运算法则字符串,遇到右括号则弹出栈中前两个矩阵,计算测试,并更新前面个矩阵的行列为运算后的矩阵的行列,遇到左括号不做任何事,遇到其他字符就加入栈中。
这里是默认了一个左括号匹配一个右括号,运算式不会有问题,题目也是这样安排的。
#include<iostream>
#include<stack>
#include<string>
#include<vector>
using namespace std;
int main(){
int n;
while(cin >> n){
vector<pair<int, int> > matrix(n);
string rule;
for(int i = 0; i < n; i++) //输入n个矩阵的行列数
cin >> matrix[i].first >> matrix[i].second;
cin >> rule; //输入运算法则
stack<char> s; //记录代表矩阵的字符
int count = 0;
for(int i = 0; i < rule.length(); i++){ //遍历运算法则
if(rule[i] == ')'){ //遇到右括号计算一次
if(s.size() != 1){
auto temp1 = matrix[s.top() - 'A']; //矩阵2
s.pop();
auto& temp2 = matrix[s.top() - 'A']; //矩阵1
count += temp2.first * temp2.second * temp1.second; //计算矩阵1*矩阵2的运算次数
temp2 = make_pair(temp2.first, temp1.second); //结果加入到矩阵1的位置
}
}else if(rule[i] != '(') //其他字符加入栈中代表要运算的矩阵
s.push(rule[i]);
}
cout << count << endl;
}
return 0;
}
复杂度分析:
- 时间复杂度:,遍历个矩阵,字符串长度也是级别的
- 空间复杂度:,栈的长度不会超过