惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

阮一峰的网络日志
阮一峰的网络日志
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Schneier on Security
The Last Watchdog
The Last Watchdog
Cyberwarzone
Cyberwarzone
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
Cyber Attacks, Cyber Crime and Cyber Security
L
Lohrmann on Cybersecurity
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 司徒正美
The Cloudflare Blog
V
V2EX
博客园_首页
博客园 - 聂微东
Vercel News
Vercel News
人人都是产品经理
人人都是产品经理
G
GRAHAM CLULEY
T
Tenable Blog
Last Week in AI
Last Week in AI
Y
Y Combinator Blog
L
LINUX DO - 最新话题
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
SecWiki News
SecWiki News
博客园 - 三生石上(FineUI控件)
S
Secure Thoughts
N
News | PayPal Newsroom
T
The Blog of Author Tim Ferriss
The GitHub Blog
The GitHub Blog
T
Troy Hunt's Blog
博客园 - 【当耐特】
Forbes - Security
Forbes - Security
H
Hacker News: Front Page
A
About on SuperTechFans
B
Blog RSS Feed
Engineering at Meta
Engineering at Meta
MongoDB | Blog
MongoDB | Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
罗磊的独立博客
D
DataBreaches.Net
P
Privacy & Cybersecurity Law Blog
Schneier on Security
Schneier on Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Jina AI
Jina AI
D
Docker
P
Proofpoint News Feed

OneCoder

【GESP】C++二级真题 luogu-B4553 [GESP202606 二级] 完全平方数计数 【GESP】C++一级真题 luogu-B4551 [GESP202606 一级] 去旅行 【GESP】C++一级真题 luogu-B4552 [GESP202606 一级] 交税 【NOIP】2000真题解析 luogu-P1023 税收与补贴问题(适合GESP四、五级以上练习) 【NOIP】2001真题解析 luogu-P1029 最大公约数和最小公倍数问题 【CSP】CSP-X 2018真题 11的倍数 luogu-B4075 (适合GESP三级及以上考生练习) 【CSP】CSP-X 2018真题 统计成绩 luogu-B4074 (适合GESP二级及以上考生练习) 【CSP】CSP-X 2018真题 快递费用 luogu-B4073 (适合GESP二级及以上考生练习) 【CSP】CSP-X 2018真题 小明的照片 luogu-B4072 (适合GESP一级及以上考生练习) 【GESP】C++四级练习 luogu-P1138 第 k 小整数 【NOIP】2008真题解析 luogu-P1125 笨小猴 【信奥业余科普】C++ 的奇妙之旅 29:别让 TLE 和 MLE 偷走你的分——复杂度估算与数据范围速查 【信奥业余科普】C++ 的奇妙之旅 28:规范比赛代码的钥匙——文件操作与输入输出重定向(freopen) 【CSP】CSP-J 2023真题 公路 luogu-P9749 (适合GESP四级及以上考生练习) 【信奥业余科普】C++ 的奇妙之旅 27:高效处理数据的利器——常用算法库(algorithm) 【CSP】CSP-J 2022真题 解密 luogu-P8814 (适合GESP四级及以上考生练习) 【信奥业余科普】C++ 的奇妙之旅 26:高效的键值对——映射(map)与多重映射(multimap) 【CSP】CSP-J 2022真题 乘方 luogu-P8813 (适合GESP二级及以上考生练习) 【信奥业余科普】C++ 的奇妙之旅 25:自动排序的利器——集合(set)与多重集合(multiset) 【CSP】CSP-J 2019真题 纪念品 luogu-P5662 (适合GESP六级及以上考生练习) 【信奥业余科普】C++ 的奇妙之旅 24:拆解 deque——分段连续的双端队列 【信奥业余科普】C++ 的奇妙之旅 23:主动限制的艺术——栈(stack)与队列(queue)
【NOIP】2000真题解析 luogu-P1022 计算器的改良(适合GESP四、五级以上练习)
OneCoder · 2026-06-25 · via OneCoder

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

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

题目要求

题目描述

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

一元一次方程的实例:

  • $4+3x=8$
  • $6a-5+1=2-2a$
  • $-5+12y=0$

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

输入格式

一个一元一次方程。

输出格式

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

输入输出样例 #1

输入 #1
输出 #1

【题目来源】

NOIP 2000 普及组第二题


题目分析

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

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

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

\[\text{coeff} \times x = \text{constant}\]

所有含未知数的项移到等号左边,所有常数项移到等号右边,最终 $x = \text{constant} / \text{coeff}$。

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

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

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

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

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

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

遍历每个字符时:

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

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

3. 边界情况

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

4. 样例演绎

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

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

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

汇总:$\text{coeff} = 6 + 2 = 8$,$\text{constant} = 5 - 1 + 2 = 6$。

结果:$a = 6 / 8 = 0.750$ ✅

5. 易错点:负零陷阱

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

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

  • -a=0:$\text{coeff} = -1$,$\text{constant} = 0$,$0 / (-1) = -0.0$ ❌
  • 0=x:$\text{coeff} = -1$,$\text{constant} = 0$,$0 / (-1) = -0.0$ ❌

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

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

6. 复杂度分析

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

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#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;
}


所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code

GESP 学习专题站:GESP WIKI

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

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

欢迎加入Java、C++、Python技术交流QQ群(982860385),大佬免费带队,有问必答

欢迎加入C++ GESP/CSP认证学习QQ频道,考试资源总结汇总

欢迎加入C++ GESP/CSP学习交流QQ群(688906745),考试认证学员交流,互帮互助

GESP/CSP 认证学习微信公众号

GESP/CSP 认证学习微信公众号