题解:洛谷 P1957 口算练习题

【题目来源】

洛谷:P1957 口算练习题 - 洛谷

【题目描述】

王老师正在教简单算术运算。细心的王老师收集了 \(i\) 道学生经常做错的口算题,并且想整理编写成一份练习。编排这些题目是一件繁琐的事情,为此他想用计算机程序来提高工作效率。王老师希望尽量减少输入的工作量,比如 \(5+8\) 的算式最好只要输入 \(5\)\(8\),输出的结果要尽量详细以方便后期排版的使用,比如对于上述输入进行处理后输出 \(5+8=13\) 以及该算式的总长度 \(6\)。王老师把这个光荣的任务交给你,请你帮他编程实现以上功能。

【输入】

第一行为数值 \(i\)

接着的 \(i\) 行为需要输入的算式,每行可能有三个数据或两个数据。

若该行为三个数据则第一个数据表示运算类型,\(a\) 表示加法运算,\(b\) 表示减法运算,\(c\) 表示乘法运算,接着的两个数据表示参加运算的运算数。

若该行为两个数据,则表示本题的运算类型与上一题的运算类型相同,而这两个数据为运算数。

【输出】

输出 \(2\times i\) 行。对于每个输入的算式,输出完整的运算式及结果,第二行输出该运算式的总长度

【输入样例】

4
a 64 46
275 125
c 11 99
b 46 64

【输出样例】

64+46=110
9
275+125=400
11
11*99=1089
10
46-64=-18
9

【核心思想】

  1. 问题分析:给定 \(i\) 行输入,每行可能是“操作符 + 两个数”(三个数据)或“两个数”(两个数据)。若为两个数据,则沿用上一行的操作符。需要根据操作符(a 加、b 减、c 乘)计算表达式,并输出完整的算式(如 5+8=13)及其长度(字符数)。本质是字符串格式化与状态保持问题,核心在于处理变长输入并维护当前操作符。

  2. 算法选择

    • 字符串流 + sprintf:使用 stringstream 将字符串转换为数字,用 sprintf 格式化输出算式。这是 C/C++ 中处理字符串格式化的常用方法。
    • 状态维护:使用变量 flag 存储当前操作符,若某行只有两个数据,则沿用 flag
  3. 关键步骤

    • 读入:读取第一行整数 \(n\)(操作数)。
    • 循环处理每行
      • 读入一个字符串 s
      • 判断 s 是否为操作符("a""b""c"):
        • 若是,则更新 flag = s[0],并读入两个整数 a, b
        • 若不是,则说明 s 是第一个操作数,需要将其转换为整数 a,并读入第二个整数 b
      • 根据 flag 计算表达式,并使用 sprintf 生成算式字符串 s2
      • 输出 s2 和它的长度(strlen(s2))。
    • 注意:使用 stringstream 时,每次转换前需调用 stream.clear() 清空状态。
  4. 时间/空间复杂度

    • 时间复杂度:\(O(i \times L)\),其中 \(L\) 是算式字符串长度(常数),\(i\) 是行数。
    • 空间复杂度:\(O(1)\),只使用了固定大小的字符数组。
  5. 字符串格式化与输入处理

    • 变长输入处理:通过判断读入的第一个 token 是字母还是数字,来决定是否更新操作符。这是处理“状态依赖型输入”的常用技巧。
    • sprintf 的便利性:可以方便地按指定格式拼接字符串和数字,生成 "a+b=c" 形式的算式。
    • 字符串长度计算strlen(s2) 返回字符数组的字符数(不含末尾的 \0),正好是算式长度。
    • 适用场景:这类“格式化输出 + 变长输入”的模拟题,常见于需要保持上下文中操作符状态的情况。

【算法标签】

普及- #字符串入门

【代码详解】

#include <bits/stdc++.h>  // 包含标准库头文件(万能头文件)
using namespace std;      // 使用标准命名空间

int main()
{
    string s;              // 定义字符串:存储输入的命令或数字
    stringstream stream;   // 定义字符串流:用于字符串和数字的转换
    char s2[1000];         // 定义字符数组:存储格式化后的算式字符串
    int n, a, b;           // 定义变量:n-操作次数,a和b-操作数
    char flag;             // 定义变量:存储操作类型(a加/b减/c乘)

    cin >> n;              // 输入操作次数

    // 处理每次操作
    for (int i = 1; i <= n; i++)
    {
        cin >> s;          // 输入命令或第一个数字

        // 判断输入的是命令(a/b/c)还是数字
        if (s[0] >= 'a' && s[0] <= 'c')
        {
            flag = s[0];   // 存储操作类型
            cin >> a >> b; // 输入两个操作数
        }
        else
        {
            // 将字符串转换为数字
            stream.clear(); // 清空流状态
            stream << s;    // 将字符串写入流
            stream >> a;    // 从流读取第一个数字
            cin >> b;       // 输入第二个数字
        }

        // 根据操作类型格式化算式字符串
        if (flag == 'a')
        {
            sprintf(s2, "%d+%d=%d", a, b, a + b);  // 加法算式
        }
        else if (flag == 'b')
        {
            sprintf(s2, "%d-%d=%d", a, b, a - b);  // 减法算式
        }
        else
        {
            sprintf(s2, "%d*%d=%d", a, b, a * b);  // 乘法算式
        }

        // 输出结果
        cout << s2 << endl << strlen(s2) << endl;  // 算式和算式长度
    }

    return 0;              // 程序正常结束
}

【运行结果】

4
a 64 46
275 125
c 11 99
b 46 64
64+46=110
9
275+125=400
11
11*99=1089
10
46-64=-18
9
posted @ 2026-08-30 09:17  团爸讲算法  阅读(10)  评论(0)    收藏  举报