题解:B4278 [蓝桥杯青少年组国赛 2023] 简单算术题

· · 题解

Solution

题目解析

这道题给我们一个只包含四则运算符(+ - \times \div)和数字的表达式,没有括号,而且要求我们:

  1. 先算乘除,再算加减
  2. 同级运算从左到右
  3. 除法只保留整数部分(例如 5 \div 2 = 2,忽略小数部分)。

「先乘除后加减」其实是很多运算场景的常识,但平时我们会用括号或者写在不同行来区分运算顺序;本题恰好没给括号,就需要我们自己按照这种顺序来运算。

举个例子:

2 + 34 - 10 \div 6 + 1 \div 24

所以整个表达式的值就是 13

解题思路

为了实现「先乘除、后加减」,可以把表达式拆分成一段段“连续的乘除块”,然后用加减符号把这些块组合起来:

  1. 从头扫描到尾,每当遇到 +-,就意味着「前面那一小段乘除运算已经结束了」。
  2. 对每个“块”里的 \times\div,就按照从左到右的顺序一步步算,保证了乘除是按正确的顺序执行的——只要不碰到 +-,就一直在乘除之内。
  3. 最后,当我们真正遇到 +- 时,把这一块的运算结果整体地加到(或者减到)最终的答案上。
  4. 当表达式扫描完毕后,如果还有一小段乘除没合并到总结果,也要补上去。

这样就能保证先把乘除算完,再统一做加减,而且每一段乘除的结果用一个临时变量维护。

除法保留整数部分可以直接用 C++ 语言中的整除特性:10 \div 6 自动得到 1,不会保留小数。

AC代码

#include <iostream>
#include <string>
#define ll long long
using namespace std;

ll clac() {
    ll mul = 1;       // 储存当前“乘除块”的中间结果
    ll res = 0;       // 最终累加结果
    char c;           // 用来逐个读取字符
    ll t = 0;         // 正在读取的数字
    bool flag = false;// 标记上一运算符是不是 '/'

    // 循环读取每个非空字符
    while (cin >> c) {
        // 如果读到数字,就组装到 t
        if (c >= '0' && c <= '9') {
            t = t * 10 + (c - '0');
        }
        // 遇到加号 '+',把这段“乘除”结算进res,开启新的段
        else if (c == '+') {
            if (flag) {     // 如果前一运算符是除号
                mul /= t;
                flag = false;
            } else {        // 如果前一运算符是乘号
                mul *= t;
            }
            res += mul;     // 这一块算完了,加到res
            mul = 1;        // 开启新块 (正号块)
            t = 0;          // 清空t以便下次读数字
        }
        // 遇到减号 '-',同理
        else if (c == '-') {
            if (flag) {
                mul /= t;
                flag = false;
            } else {
                mul *= t;
            }
            res += mul;
            mul = -1;       // 准备新的块,但带一个负号
            t = 0;
        }
        // 遇到乘号 '*'
        else if (c == '*') {
            if (flag) {
                // 前一次是除号,需要先把那次除法算完
                mul /= t;
                flag = false;
            } else {
                // 前一次是乘号
                mul *= t;
            }
            t = 0;
        }
        // 遇到除号 '/'
        else if (c == '/') {
            if (flag) {
                // 如果仍旧是除法延续,先把之前的除法做完
                mul /= t;
            } else {
                // 否则先做乘法,再进入除法状态
                mul *= t;
            }
            flag = true;  // 标记:下次遇到数字时先完成除法
            t = 0;
        }
    }

    // 表达式读完,还有最后一段乘除要合并
    if (flag) {
        mul /= t;
        flag = false;
    } else {
        mul *= t;
    }
    res += mul;

    return res;
}

int main() {
    cout << clac() << "\n";
    return 0;
}

设字符串长度为 n,那么时间复杂度为 O(n)。可以通过此题。