8 数位处理与逻辑模拟(一)
正在加载视频播放器…
GESP C++ 二级:数位处理与逻辑模拟综合
核心目标:掌握数位拆分核心技术,理解逻辑模拟的方法论,能综合运用循环、分支、数位处理解决模拟类问题,熟悉GESP二级高频真题的解题思路。
数位处理靠“取余取位、整除去位”;逻辑模拟靠“忠实复现题目描述的规则和流程”;两者结合,就是GESP二级编程题的核心考查方向。
第一部分:数位处理核心技术与高频题型
数位处理是GESP二级编程题中出现频率最高的考点之一。核心操作只有两个:% 10 取个位,/ 10 去个位。
1. 数位处理六大题型
| 题型 | 核心操作 | 典型真题 |
|---|---|---|
| 求各位数字之和 | 循环累加 n % 10 |
[GESP202412 二级] 寻找数字 |
| 求数字个数(位数统计) | 循环计数,注意0特判 | 拆数类题目 |
| 数字反转 | rev = rev * 10 + n % 10 |
逆序数问题 |
| 数位统计(统计某数字出现次数) | 逐位判断是否等于目标值 | [GESP202603 二级] 数数 |
| 特殊数字判断(水仙花数/自幂数) | 数位拆分 + 幂运算 | [GESP202306 二级] 自幂数判断 |
| 数位比较(回文数/平衡数/递增数) | 拆分后比较或反转比较 | 回文数、平衡数 |
2. 标准数位处理模板
// 模板1:数位求和
int digitSum(int n) {
int sum = 0;
while (n > 0) {
sum += n % 10;
n /= 10;
}
return sum;
}
// 模板2:数字反转
int reverseNum(int n) {
int rev = 0;
while (n > 0) {
rev = rev * 10 + n % 10;
n /= 10;
}
return rev;
}
// 模板3:统计某数字出现次数
int countDigit(int n, int target) {
int cnt = 0;
while (n > 0) {
if (n % 10 == target) cnt++;
n /= 10;
}
return cnt;
}
// 模板4:位数统计(注意0特判)
int digitCount(int n) {
if (n == 0) return 1;
int cnt = 0;
while (n > 0) {
cnt++;
n /= 10;
}
return cnt;
}
3. 真题示例:数位和为7的倍数
题目来源:GESP 2024年9月 二级真题“数位之和”
题目要求:输入n个整数,对每个数求各位数字之和,判断是否为7的倍数。
#include <cstdio>
using namespace std;
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
long long a;
scanf("%lld", &a);
int sum = 0;
while (a > 0) {
sum += a % 10;
a /= 10;
}
if (sum % 7 == 0)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}
考试提示:当输入数据量较大(如10^5次)时,
cin/cout可能超时,建议使用scanf/printf提升效率。
4. 真题示例:自幂数判断
题目来源:GESP 2023年6月 二级真题“自幂数判断”
自幂数:一个n位数,其各位数字的n次方之和等于它本身。
#include <iostream>
#include <cmath>
using namespace std;
int main() {
long long n;
cin >> n;
long long t = n;
int digits = 0;
while (t > 0) { digits++; t /= 10; }
t = n;
long long sum = 0;
while (t > 0) {
int d = t % 10;
sum += (long long)pow(d, digits);
t /= 10;
}
if (sum == n) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
5. 真题示例:数数
题目来源:GESP 2026年3月 二级真题“数数”
题目要求:判断正整数n的所有数位中是否包含恰好3个2。
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int cnt = 0;
int t = n;
while (t > 0) {
if (t % 10 == 2) cnt++;
t /= 10;
}
if (cnt == 3) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
这类题目考察的是嵌套循环与数位分离的综合运用:外层遍历范围,内层拆解数字,统计满足条件的数字。
第二部分:逻辑模拟方法论
6. 什么是逻辑模拟
模拟算法是GESP二级最具实践价值的模块,强调将现实问题流程逐行映射为代码指令,不依赖数学公式推导,而依靠程序精确控制每一步状态变迁。
模拟类题目在GESP真题中占比超过35%,且极易因“状态遗漏”或“时序错乱”导致全盘错误。
7. 模拟算法的核心步骤
分析问题 → 识别状态变量 → 确定状态更新规则 → 确定终止条件 → 编写循环/分支 → 验证边界
| 步骤 | 说明 | 示例 |
|---|---|---|
| 识别状态变量 | 哪些量会随过程变化 | 位置x/y、方向dir、剩余步数step |
| 确定更新规则 | 每一步如何改变状态 | 每步x += dx, y += dy |
| 确定终止条件 | 什么时候停止 | 步数用完、到达终点、条件满足 |
| 处理边界 | 越界、碰撞、非法输入 | 坐标越界检查、除零检查 |
| 调试 | 打印每步状态辅助纠错 | cout << "step:" << step << " x:" << x; |
8. 常见模拟模型
| 模型 | 状态变量 | 典型题目 |
|---|---|---|
| 数字变换游戏 | 当前数字 | 卡拉兹猜想、数字黑洞 |
| 日历星期推算 | 天数、星期 | 日期计算 |
| 迷宫行走 | x, y, 方向 | 小球滚动路径 |
| 排队叫号系统 | 队列状态、当前号 | 银行叫号 |
| 矩阵遍历 | 行i、列j | X字矩阵、N字矩阵 |
9. 真题示例:数字黑洞
题目来源:GESP 2023年9月 二级真题“数字黑洞”
数字黑洞:对一个数字,将其各位数字重新排列成最大数和最小数,相减,重复直到得到固定值。
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
int cnt = 0;
while (n != 495 && n != 6174) { // 3位数黑洞495,4位数黑洞6174
int digits[4], t = n, len = 0;
while (t > 0) { digits[len++] = t % 10; t /= 10; }
// 补零到位数统一
while (len < 4) digits[len++] = 0;
sort(digits, digits + 4);
int minNum = 0, maxNum = 0;
for (int i = 0; i < 4; i++) {
minNum = minNum * 10 + digits[i];
maxNum = maxNum * 10 + digits[3 - i];
}
n = maxNum - minNum;
cnt++;
}
cout << cnt << endl;
return 0;
}
模拟要点:每一步都要忠实复现规则——拆位、排序、组合最大最小数、相减,一步不能少。
10. 真题示例:卡拉兹猜想
题目要求:对正整数n,若为偶数则除以2,若为奇数则(3n+1)除以2,重复直到n=1,统计次数。
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int cnt = 0;
while (n != 1) {
if (n % 2 == 0)
n /= 2;
else
n = (3 * n + 1) / 2;
cnt++;
}
cout << cnt << endl;
return 0;
}
这是最典型的逻辑模拟:规则清楚,只需按规则一步步执行,不需要任何数学推导。
第三部分:数位处理与逻辑模拟综合应用
11. 综合题解题框架
┌─────────────────────────────────────┐
│ 外层循环:遍历范围或重复模拟次数 │
│ ├─ 输入/初始化状态变量 │
│ ├─ 内层循环:数位拆分 / 状态更新 │
│ ├─ 条件判断:筛选/计数/累加 │
│ └─ 输出/更新结果 │
└─────────────────────────────────────┘
12. 综合模型一:遍历范围内数位满足条件的数
题目:输出1~n中,数位之和为偶数的所有数。
for (int i = 1; i <= n; i++) {
int t = i, sum = 0;
while (t > 0) {
sum += t % 10;
t /= 10;
}
if (sum % 2 == 0) {
cout << i << " ";
}
}
考点:外层遍历 + 内层数位拆分 + 条件判断。
13. 综合模型二:模拟数字变换过程
题目:从n开始,每次将n替换为其数位之和,直到n为一位数,输出最终结果和步数。
int n;
cin >> n;
int steps = 0;
while (n >= 10) {
int sum = 0;
while (n > 0) {
sum += n % 10;
n /= 10;
}
n = sum;
steps++;
}
cout << n << " " << steps << endl;
14. 综合模型三:好数判断(数位与位数关系)
题目:好数是指数字的数位之和是位数的倍数。
int n;
cin >> n;
int t = n, sum = 0, digits = 0;
while (t > 0) {
sum += t % 10;
t /= 10;
digits++;
}
if (sum % digits == 0) cout << "Yes" << endl;
else cout << "No" << endl;
15. 综合模型四:平衡数判断
题目:平衡数是指奇数位数字之和与偶数位数字之和相等。
int n;
cin >> n;
int t = n, pos = 1, oddSum = 0, evenSum = 0;
while (t > 0) {
int d = t % 10;
if (pos % 2 == 1) oddSum += d;
else evenSum += d;
t /= 10;
pos++;
}
if (oddSum == evenSum) cout << "Yes" << endl;
else cout << "No" << endl;
16. 综合模型五:递增数判断
题目:递增数是指数字从左到右每一位高位数字比低位数字小。
int n;
cin >> n;
int last = 10; // 比任何一位数字都大
bool ok = true;
while (n > 0) {
int d = n % 10; // 从个位开始取(相当于从右往左)
if (d >= last) { // 右边应比左边小
ok = false;
break;
}
last = d;
n /= 10;
}
if (ok) cout << "Yes" << endl;
else cout << "No" << endl;
注意:从右往左取数位时,判断条件要反过来理解——先取的是最低位,后取的是更高位,递增要求高位 < 低位。
第四部分:易错点与调试技巧
17. 数位处理易错点
- 忘记特判0:0的位数是1,不是0。
while (n > 0)对0不执行。 - 变量被修改后无法再用:数位拆分时会修改原数,需要用临时变量
int t = n;保存。 - 数字反转溢出:反转后的数可能超出
int范围,大数用long long。 - 取位顺序搞反:
n % 10先取个位(最低位),不是最高位。 - 数位比较方向错误:从右往左取位时,递增/递减的判断方向要相应调整。
18. 逻辑模拟易错点
- 状态遗漏:模拟过程中某些状态没有更新,导致结果错误。
- 时序错乱:状态更新的顺序影响结果,先更新谁后更新谁要想清楚。
- 终止条件错误:死循环或提前退出。
- 边界未处理:坐标越界、除零、输入为0等特殊情况。
- 输出格式错误:换行、空格、大小写不符合题目要求。
19. 调试技巧
打印中间状态:在循环内插入输出语句,观察每一步的变量变化。
while (n > 0) {
int d = n % 10;
cout << "digit=" << d << " n=" << n << endl; // 调试输出
// ...
n /= 10;
}
小数据验证:用简单输入(如n=1、n=10、n=0)先验证程序基本逻辑,再测试大数据。
总结
数位处理与逻辑模拟是GESP二级编程题的核心考查方向。
- 数位处理核心:
% 10取个位,/ 10去个位。 - 数位拆分时用临时变量保存原数。
- 0的位数是1,注意特判。
- 数字反转用
rev = rev * 10 + n % 10。 - 逻辑模拟的关键是“忠实复现题目描述”。
- 模拟前先识别状态变量和更新规则。
- 模拟类题目占比高,必须熟练掌握。
- 调试时打印中间状态,小数据验证。
- 大数据输入用
scanf/printf提升效率。 - 综合题 = 外层遍历 + 内层数位处理 + 条件判断。