乐于分享
好东西不私藏

【NOIP】2000真题解析 luogu-P1022 计算器的改良(适合GESP四、五级以上练习)

【NOIP】2000真题解析 luogu-P1022 计算器的改良(适合GESP四、五级以上练习)
科普类资源清单
🤞信奥业余科普

GESP学习资源清单

真题
练习题
考纲解析
一级真题解析一级练习题清单111-8级全考纲解析
二级真题解析二级练习题清单GESP/CSP必备技能
三级真题解析三级练习题清单考纲解密
四级真题解析四级练习题清单资源汇总/经验交流
五级真题解析五级练习题清单
六级真题解析六级练习题清单


CSP学习资源清单

CSP-XL
CSP-J
2025辽宁CSP-XL复赛真题解析CSP-J 真题解析
CSP-X 真题解析

NOI学习资源清单
NOIP
1998年真题解析‍‍
1999年真题解析
2000年真题解析
2001年真题解析‍‍
2008年真题解析
2011年真题解析

NOIP 2000 普及组真题,考察字符串解析一元一次方程求解。解题核心是逐字符扫描方程字符串,将含未知数的项(系数)与常数项分别累加到方程两侧,最终求解 。适合GESP四、五级以上考生练习。题目难度⭐⭐⭐,洛谷难度等级普及/提高−

luogu-P1022 [NOIP 2000 普及组] 计算器的改良

题目要求

题目描述

NCL 是一家专门从事计算器改良与升级的实验室,需要在计算器上加上解一元一次方程的功能。

一元一次方程的实例:

方程中只包含整数、小写字母及 +-= 这三个数学符号(符号 - 既可作减号,也可作负号)。方程中没有括号和除号,字母表示未知数。方程均为合法的,且有唯一实数解。

输入格式

一个一元一次方程。

输出格式

解方程的结果(精确至小数点后三位)。

输入输出样例 #1

输入 #1
6a-5+1=2-2a
输出 #1
a=0.750

【题目来源】

NOIP 2000 普及组第二题


题目分析

这道题的核心是字符串解析 + 数学建模:将方程字符串拆解为若干"项",每一项要么是常数项,要么是含未知数的系数项,然后将所有系数项归到等号一侧、常数项归到另一侧,最终做一次除法即可。

1. 整体思路——移项求解

对于一元一次方程,我们的目标是将其化简为:

所有含未知数的项移到等号左边,所有常数项移到等号右边,最终 

为了实现移项,我们引入一个 side 变量:

  • 在等号左侧时,side = 1:系数直接加到 coeff,常数项变号后加到 constant(移到右边)。
  • 在等号右侧时,side = -1:常数项直接加到 constant,系数变号后加到 coeff(移到左边)。

这样只需一次从左到右的扫描,就能同时完成移项和累加。

2. 逐字符扫描——项的识别

方程由若干"项"组成,每一项以 +- 或 = 作为分隔。扫描过程中需要维护以下状态:

变量
含义
sign
当前项的正负号(+1 或 -1)
num
当前正在积累的数值
hasNum
是否已读取到数字(区分系数为 0 还是省略)

遍历每个字符时:

  • 数字:累积到 num,标记 hasNum = true
  • 字母:说明当前项是未知数项。若 hasNum 为 false(如 a-a),则系数为 (或 );否则系数为 num。将 side × sign × 系数 累加到 coeff
  • **+ 或 -**:说明前一项结束。如果前一项是纯数字(没有遇到字母),则作为常数项处理:将 -side × sign × num 累加到 constant(注意负号,因为要移项到右边)。然后重置 numhasNum,并更新 sign
  • **=**:与 +/- 类似处理当前项,然后将 side 改为 sign 重置为 

循环结束后,别忘了处理最后一项(因为方程末尾没有 +/-/= 来触发处理)。

3. 边界情况

  • 省略系数:如 a 表示 -a 表示 。需要通过 hasNum 标记来区分。
  • 开头为负号:如 -5+12y=0,此时第一个 - 是负号而非减号。扫描逻辑天然支持这种情况——初始 sign = 1,遇到 - 更新 sign = -1
  • 输出格式:先输出字母名,再输出 =,最后输出三位小数。需要找到方程中出现的那个字母。

4. 样例演绎

以输入 6a-5+1=2-2a 为例:

side
sign
类型
对 coeff 的贡献
对 constant 的贡献
6a
+1
+1
未知数
-5
+1
-1
常数
+1
+1
+1
常数
+2
-1
+1
常数
(注:右侧常数直接加)
-2a
-1
-1
未知数
(注:右侧系数变号移左)

注:上面表格为了直观展示移项逻辑做了简化。实际代码中,左侧常数用 -side * sign * num 累加到 constant,右侧系数用 side * sign * num 累加到 coeffside = -1 时自动完成变号。

汇总:

结果: ✅

5. 易错点:负零陷阱

在 IEEE 754 浮点数标准中,0.0 / (-1.0) 的结果是 **-0.0**(负零)。当使用 printf("%.3f", -0.0) 输出时,会打印 -0.000,而 OJ 期望的是 0.000

这种情况出现在方程解为 ,但系数为负的场景中,例如:

  • -a=0 ❌
  • 0=x ❌

修复方法:在输出前判断结果是否为 ,若是则强制置为正零 0.0。本题系数和常数均为整数,不存在浮点精度问题,直接用 == 0.0 判断即可(IEEE 754 中 -0.0 == 0.0 为 true):

double result = constant / coeff;if (result == 0.0) {    result = 0.0// 避免输出 -0.000}

6. 复杂度分析

  • 时间复杂度 为方程字符串长度,单次扫描即可。
  • 空间复杂度,只使用常数个变量。

示例代码

#include <cstdio>#include <cstring>int main() {    char eq[1000];    scanf("%s", eq);    int len = strlen(eq);    // 找到未知数字母    char var = 0;    for (int i = 0; i < len; i++) {        if (eq[i] >= 'a' && eq[i] <= 'z') {            var = eq[i];            break;        }    }    double coeff = 0;    // 未知数系数之和(归到左侧)    double constant = 0// 常数之和(归到右侧)    int side = 1;        // 等号左侧 +1,右侧 -1    int sign = 1;        // 当前项的正负号    int num = 0;         // 当前积累的数值    bool hasNum = false; // 是否已读取到数字    for (int i = 0; i < len; i++) {        char c = eq[i];        if (c >= '0' && c <= '9') {            // 累积数字            num = num * 10 + (c - '0');            hasNum = true;        } else if (c == var) {            // 遇到未知数字母,处理系数项            if (!hasNum) {                num = 1// 省略系数时,系数为 1            }            coeff += side * sign * num;            // 重置            num = 0;            hasNum = false;            sign = 1;        } else if (c == '+' || c == '-') {            // 遇到运算符,先处理前一项(如果是常数项)            if (hasNum) {                // 前一项是纯数字(常数项),移到右边(取反)                constant -= side * sign * num;            }            // 重置并更新符号            num = 0;            hasNum = false;            sign = (c == '+') ? 1 : -1;        } else if (c == '=') {            // 处理等号前的最后一项            if (hasNum) {                constant -= side * sign * num;            }            // 切换到等号右侧            side = -1;            num = 0;            hasNum = false;            sign = 1;        }    }    // 处理方程末尾的最后一项    if (hasNum) {        constant -= side * sign * num;    }    // 计算结果,处理负零问题    double result = constant / coeff;    if (result == 0.0) {        result = 0.0// 避免输出 -0.000    }    // 输出结果,精确到小数点后三位    printf("%c=%.3f\n", var, result);    return 0;}

【推荐】【GESP】C++ 认证学习资源汇总(2026年3月更新)

【推荐】GESP/CSP/NOI资料站https://wiki.coderli.com/

【推荐】GESP/CSP学习交流群

luogu-”系列题目可在洛谷题库进行在线评测。

bcqm-”系列题目可在编程启蒙题库进行在线评测。