【华为od刷题(C++)】HJ70 矩阵乘法计算量估算(emplace函数、栈)

我的代码:

#include <iostream>//用于输入输出流的操作

#include <vector>//用于动态数组的操作

#include <map>//包含了 C++ 标准库中的 map 容器
/*map 是一种关联容器,用于存储键值对(key-value pairs),
并且每个键只能出现一次,键值对会根据键的大小自动排序*/

#include <stack>//是 C++ 标准库中的 stack 容器的头文件
/*stack 是一种后进先出(LIFO, Last In First Out)数据结构,
它允许在容器的末端进行元素的插入和删除操作*/

using namespace std;

int main() {
    int n;//n 表示矩阵的数量

    while (cin >> n) {
        //定义了一个整型变量 n,并且通过 cin 从输入流读取数据
        //while(cin >> n) 使得程序会一直运行直到没有更多输入

        vector<vector<int>> m(n, vector<int>(2, 0));
        /*用来存储每个矩阵的维度信息
        这里使用的是二维 vector*/

        for (int i = 0; i < n; ++i) {//读取每个矩阵的维度

            cin >> m[i][0] >> m[i][1];
            //m[i][0] 存储矩阵的行数,m[i][1] 存储矩阵的列数
        }

        stack<vector<int>> st;
        /*用于存储矩阵的维度
        栈中的每一项是一个包含矩阵行列数的 vector<int>,
        用于模拟矩阵的操作*/

        int ans = 0;//用于累加计算矩阵乘法的总运算次数

        for (int i = 0; i < 3 * n - 2; ++i) {
            //字符串的长度为 n + 2*(n-1)

            char ch;//用来存储每个字符

            cin >> ch;
            //从输入流读取一个字符(这个字符可能是 (, ), 或者字母)

            if (ch == '(') {
                continue;
                //如果字符是左括号 '(',则跳过不做处理
                //(左括号不影响栈的操作)

            } else if (ch == ')') {
                //如果字符是右括号 ')',就需要进行矩阵乘法的计算

                vector<int> a(2, 0);
                vector<int> b(2, 0);
                /*定义了两个 vector<int>,a 和 b,
                它们都初始化为含有 2 个元素且值为 0 的向量

                每个向量的大小是 2,表示存储一个矩阵的维度信息:
                a[0] 和 a[1] 存储矩阵 A 的维度(A 的行数和列数)
                b[0] 和 b[1] 存储矩阵 B 的维度(B 的行数和列数)*/

                b = st.top();
                /*st.top() 获取栈顶元素
                (即栈中最新压入的矩阵维度)

                将栈顶的矩阵维度赋值给 b,表示矩阵 B 的维度*/

                st.pop();//通过 st.pop() 将该元素从栈中弹出

                a = st.top();
                //*将栈顶的矩阵维度赋值给 a,表示矩阵 A 的维度

                st.pop();//将矩阵 A 的维度从栈中移除

                ans += a[0] * a[1] * b[1];
                //用于计算两个矩阵相乘时的乘法运算次数
                /*假设矩阵 A 的维度是 a[0] x a[1],
                矩阵 B 的维度是 b[0] x b[1]

                两个矩阵相乘时,计算的运算次数是 
                A 的行数(a[0])乘以 A 的列数(a[1])和 B 的列数(b[1])
                即,矩阵乘法的基本运算量为 a[0] * a[1] * b[1]
                将这个运算量加到 ans 上,表示当前计算中所需的乘法次数*/

                //将计算后的新矩阵大小压入栈内
                st.emplace(vector<int> {a[0], b[1]});
                /*创建了一个新的 vector<int> 对象,
                并用 {a[0], b[1]} 来初始化它
                
                emplace 是 STL 容器 stack 提供的一种方法,
                它可以直接在栈中构造一个对象,
                而不需要先创建该对象并再将其压入栈中

                emplace 相比于 push 更高效,
                因为它避免了创建临时对象的步骤*/

            } else {
                st.emplace(m[ch - 'A']);//字母,使对应的矩阵大小入栈
                /*这里的 ch - 'A' 会将字母 ch 转换成其对应的索引
               (例如,如果 ch 是 'A',那么 ch - 'A' 就是 0,'B' 是 1,依此类推)

                m[ch - 'A'] 返回 m 数组中第 ch - 'A' 行的矩阵维度
               (即一个包含两个整数的 vector,代表矩阵的行数和列数)*/
            }
        }
        cout << ans << endl;
    }
    return 0;
}

思路总结

这个程序的目标是计算给定一系列矩阵乘法所需的最小运算次数;程序通过模拟矩阵链乘法问题(Matrix Chain Multiplication),利用栈来跟踪矩阵的维度,并根据括号内的操作顺序计算运算量

主要步骤:

  1. 输入矩阵维度

    • 输入一个整数 n,表示矩阵的数量
    • 然后为每个矩阵输入两个整数,表示矩阵的行数和列数,存储在二维 vector<int> 中
  2. 使用栈跟踪矩阵维度

    使用一个 stack 来存储矩阵的维度信息;每次遇到左括号 ( 时,栈中的矩阵维度不会发生变化;遇到右括号 ) 时,才会进行矩阵乘法的计算
  3. 处理矩阵乘法

    • 矩阵乘法的规则是:给定两个矩阵 A (a1 × a2) 和 B (b1 × b2),它们能够相乘当且仅当 A 的列数等于 B 的行数(即 a2 = b1);矩阵乘法的运算量是 a1 * a2 * b2
    • 每次遇到 ),程序会从栈中弹出两个矩阵的维度,计算乘法的运算量,然后将新得到的矩阵(结果矩阵)的维度压回栈中
  4. 计算总运算量

    计算过程中,累加每一次矩阵乘法所需的运算次数,并最终输出总的运算量

核心实现:

  • 栈的操作:通过 stack 容器模拟矩阵乘法操作的顺序,push 和 pop 操作分别处理矩阵维度的入栈和出栈
  • 运算量计算:每进行一次矩阵乘法时,通过计算 a[0] * a[1] * b[1] 得到运算次数,并加到总的运算量 ans 中

代码中的关键操作:

  1. 矩阵维度的存储

    • vector<vector<int>> m(n, vector<int>(2, 0)); 用于存储每个矩阵的行和列数
    • m[ch - 'A'] 将字符转化为对应的矩阵维度
  2. 栈的应用

    • stack<vector<int>> st; 用于存储矩阵维度
    • 使用 emplace 方法将矩阵的维度直接压入栈中,相比 push 更加高效
  3. 括号解析

    • 左括号 ( 不做任何操作,继续等待矩阵维度的输入
    • 右括号 ) 时,进行矩阵乘法的计算,并将计算后的新矩阵压入栈

总结:

这个程序通过栈的结构模拟了矩阵乘法的过程,利用矩阵链乘法的原理计算了最小的乘法运算次数;其实现思路清晰,充分利用了 C++ STL 容器 stackvector 来有效地解决问题

emplace 是 C++ 中 STL 容器(如 std::vectorstd::setstd::map 等)提供的一个非常有用的成员函数;它的作用是直接在容器中构造元素,而不是先创建一个临时对象再进行复制或移动插入;这样能够提高效率,尤其是对于包含复杂类型的容器

emplace 函数的特点

  1. 原地构造emplace 直接在容器的内部空间构造元素,而不需要先构造一个临时对象再进行复制或移动
  2. 避免不必要的拷贝/移动:相比 insert 等方法,emplace 不会创建额外的临时对象,因此可以避免不必要的拷贝或移动操作

基本语法

container.emplace(args...);

  • args... 是构造元素所需的参数,它们会直接传递给元素类型的构造函数

例子:std::vector

#include <iostream>
#include <vector>

int main() {
    std::vector<std::pair<int, int>> vec;

    // 使用 emplace 添加元素
    vec.emplace_back(1, 2);  // 直接在容器内部构造一个 pair<int, int> 对象

    // 输出
    for (const auto& p : vec) {
        std::cout << p.first << ", " << p.second << std::endl;
    }

    return 0;
}

在这个例子中,emplace_back(1, 2) 直接在 vec 容器中构造了一个 std::pair<int, int>,而不是先创建一个临时 pair 对象再将其添加到容器中

例子:std::set

#include <iostream>
#include <set>

int main() {
    std::set<int> st;

    // 使用 emplace 插入元素
    st.emplace(10);  // 直接在容器中构造值为 10 的元素

    // 输出
    for (const auto& e : st) {
        std::cout << e << std::endl;
    }

    return 0;
}

std::set 中,emplace 用来插入元素,set 会根据给定的值直接在内部构造元素;由于 set 中的元素不允许重复,emplace 会自动处理元素的唯一性

与 insert 的对比

  • insert 需要先创建一个对象,再将其插入到容器中:

    st.insert(10);  // 先创建一个临时元素,再插入
    
  • emplace 直接在容器中构造元素,无需临时对象:

    st.emplace(10);  // 直接在容器中构造元素
    

何时使用 emplace

  • 当容器中存储的类型比较复杂,包含多个成员,或者元素的构造比较昂贵时,emplace 会更加高效
  • 当你需要避免不必要的拷贝或移动时,emplace 是一个很好的选择
  • 如果你只需要传递参数构造元素,并且不需要先创建一个临时对象,可以选择使用 emplace

总结

emplace 是一个高效的插入方式,它避免了不必要的拷贝或移动操作,通过直接在容器内部构造元素,能够显著提高性能;特别是在存储复杂类型元素的容器中,emplace 能提供更大的优势

栈(Stack)是一种数据结构,它遵循后进先出(LIFO, Last In First Out)的原则;也就是说,最后压入栈的元素会最先被弹出

栈的主要操作

栈有两个主要的操作:

  1. 压栈(Push):将一个元素添加到栈顶
  2. 弹栈(Pop):移除栈顶的元素,并返回该元素

此外,栈通常还会提供以下辅助操作:

  • 栈顶(Top/Peek):返回栈顶的元素,但不移除它
  • 判断栈是否为空(IsEmpty):检查栈中是否还有元素
  • 栈的大小(Size):返回栈中元素的个数

栈的应用场景

栈通常用于处理具有递归结构的问题,或需要按照顺序执行某些操作的情境,常见的应用包括:

  1. 表达式求值

    在计算机科学中,栈常用于处理算术表达式的求值,尤其是在中缀表达式转后缀表达式的过程中
  2. 函数调用栈

    在程序的执行过程中,栈被用来跟踪函数的调用和返回;当一个函数被调用时,程序会将该函数的状态(如局部变量、返回地址等)压入栈中;函数执行完毕后,状态从栈中弹出,返回给调用者
  3. 回溯算法

    在解决问题时,栈可以用来保存当前的状态,并在需要时回到上一步进行尝试;这种方法常用于路径搜索、迷宫求解等
  4. 括号匹配

    栈在括号匹配算法中应用广泛,比如验证括号是否成对出现,或计算括号表达式的结果

C++ 中的栈

C++ 标准库提供了 stack 容器,位于 <stack> 头文件中;它的基本操作包括:

  • push():将元素压入栈中
  • pop():将栈顶元素弹出
  • top():返回栈顶元素
  • empty():检查栈是否为空
  • size():返回栈中的元素个数

下面是一个 C++ 栈的简单示例:

#include <iostream>
#include <stack>

using namespace std;

int main() {
    stack<int> s;

    // 向栈中压入元素
    s.push(10);
    s.push(20);
    s.push(30);

    // 打印栈顶元素
    cout << "栈顶元素: " << s.top() << endl; // 输出 30

    // 弹出栈顶元素
    s.pop();

    // 打印栈顶元素
    cout << "弹出栈顶元素后,栈顶元素: " << s.top() << endl; // 输出 20

    // 判断栈是否为空
    if (s.empty()) {
        cout << "栈为空" << endl;
    } else {
        cout << "栈不为空,栈大小为: " << s.size() << endl; // 输出 栈大小为 2
    }

    return 0;
}

栈的特点

  • 后进先出(LIFO):栈的操作遵循 LIFO 原则,保证了最新压入栈的元素最先被处理
  • 空间效率:栈通常只需要操作栈顶元素,操作简单,且空间开销较小
  • 限制性:栈只能从栈顶访问元素,无法像队列一样从两端访问,因此灵活性较低

总结来说,栈是一种非常实用的数据结构,广泛应用于需要跟踪顺序或递归操作的问题中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值