题目的主要信息:

  • 编写程序计算不同的计算顺序时矩阵乘法需要进行的乘法次数
  • 计算顺序由字符串给出,A-Z的大写字母表示矩阵,括号决定运算顺序,每次运算都有括号
  • 比如 ( ( A B ) C ) 或者 ( A ( B C ) )
  • 进阶要求:时间复杂度O(n)O(n),空间复杂度O(n)O(n)

方法一:递归

具体做法:

输入的运算规则中,字母与矩阵一一对应,如果没有括号,就是依次从左到右计算,然后结果新矩阵存在靠右的矩阵的位置,继续往右计算。加了括号之后,我们还是可以这样,只需要把括号中的问题当成子问题进入递归即可。

遍历运算法则字符串,如果是左括号我们进入括号以后开始遍历到右边,每次遇到一个左括号累增一个层数,遇到一个右括号递减一个层数,层数为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;
}

复杂度分析:

  • 时间复杂度:O(n)O(n),相当于遍历计算nn个矩阵的乘法次数
  • 空间复杂度:O(n)O(n),递归栈最坏情况下为nn

方法二:栈

具体做法:

上述递归也可以用栈来代替。 我们用一个栈记录代表矩阵的字符,遍历运算法则字符串,遇到右括号则弹出栈中前两个矩阵,计算测试,并更新前面个矩阵的行列为运算后的矩阵的行列,遇到左括号不做任何事,遇到其他字符就加入栈中。

这里是默认了一个左括号匹配一个右括号,运算式不会有问题,题目也是这样安排的。

alt

#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;
}

复杂度分析:

  • 时间复杂度:O(n)O(n),遍历nn个矩阵,字符串长度也是O(n)O(n)级别的
  • 空间复杂度:O(n)O(n),栈的长度不会超过nn