<!--markdown--># GESP 二级编程题之——拆数字(数位处理)

拆数字(数位处理)是 GESP C++ 二级的必考编程题型,核心是对整数的每一位数字进行提取、统计、计算或特征判定。常见考点覆盖数位求和、数位计数、特征数位筛选、自幂数判断等,有两种成熟的通用实现方案。

---

一、拆数字核心模板

所有数位处理题都可以套用以下两种固定模板,学过字符串的同学强烈建议用字符串。

模板1:数学法(取模/整除)

实现原理

通过循环逐次%10提取当前个位进行处理,然后/10去掉个位数字,循环终止条件为数字被缩减为 0。

标准代码模板

cpp

int num;

cin >> num;

int tmp = num; // 备份要拆的数

while(tmp != 0) // 当还没变成0的时候

{

int digit = tmp % 10; // 取出当前个位数字

// 此处对 digit 做业务处理:求和、计数、条件判断等

tmp /= 10; // 去掉个位数

}

注意事项

  • 备份变量用于拆解,不要直接对原始数字/10,避免后续用原始值出问题。
  • 若输入可能为 0,需单独处理 0 的数位逻辑,避免循环一次都不执行。

模板2:字符串法(to_string 转换 + for 遍历)

实现原理

将整数转换为字符串,通过 for 循环遍历字符串每一位,字符减去字符 '0' 即可得到对应位的整数数字。

必须修改下图配置文件,以使用 C++11 标准语法编译代码

![image.png][1]

适用场景

需要按高位到低位顺序处理、判断数位位置、统计特定数字出现次数的题目,代码直观不易出错。

标准代码模板

cpp

int n;

cin >> n;

string s = to_string(n); // 整数转字符串

int len = s.size(); // 获取数字总位数

for(int i = 0; i < len; i++)

{

int digit = s[i] - '0'; // 字符转对应整数

// 此处对 digit 做业务处理

}

注意事项

  • 字符转数字必须减去 '0',不能直接用s[i]参与数值计算。
  • 字符串下标从 0 开始,第 0 位对应数字的最高位。

---

二、真题示例

例题1:B4497 [GESP202603 二级] 数数

题目描述

对于正整数 n,如果 n 的所有数位中包含恰好 3 个 2,Alice 会认为这个正整数是美丽的。

给定区间 [L, R],求其中有多少个美丽的正整数。

输入格式

输入共 2 行,第一行为正整数 L,第二行为正整数 R。

输出格式

输出一行,包含一个整数,表示 L 到 R 中美丽数的数量。

解题思路

外层 for 循环枚举区间内每个整数,内层逐位拆分统计数字 2 出现的次数,恰好等于 3 次则答案计数加一。

参考代码(数学法)

cpp

#include <iostream>

using namespace std;

int main()

{

int L, R;

cin >> L >> R;

int ans = 0;

for(int i = L; i <= R; i++)

{

int cnt = 0; // 2的计数器,注意这句话的位置

int tmp = i; // 备份要拆的数

while(tmp != 0) // 当还没变成0的时候

{

int digit = tmp % 10; // 取出当前个位数字

if(digit == 2) // 判断并统计2的数量

cnt++;

tmp /= 10; // 去掉个位数

}

if(cnt == 3)

ans++;

}

cout << ans << endl;

return 0;

}

参考代码(字符串法)

cpp

#include <iostream>

#include <string>

using namespace std;

int main()

{

int L, R;

cin >> L >> R;

int ans = 0;

for(int i = L; i <= R; i++)

{

string s = to_string(i);

int cnt = 0;

for(int j = 0; j < s.size(); j++)

{

if(s[j] == '2')

cnt++;

}

if(cnt == 3)

ans++;

}

cout << ans << endl;

return 0;

}

---

例题2:B4411 [GESP202509 二级] 优美的数字

题目描述

如果一个正整数在十进制下的所有数位都相同,就称为优美的数字。

求不超过 n 的正整数中有多少个优美的数字。

输入格式

一行,一个正整数 n。

输出格式

一行,一个正整数,表示不超过 n 的优美正整数的数量。

解题思路

枚举 1 到 n 的所有整数,逐位判断是否所有数位都与基准数字相同,全部相同则计数加一。

用数学法相对会有点难度,需要先拆一位作为基准数,然后拆其他数位做判断。

用字符串法则比较好考虑,拿每一位和str[0],即第一位比较就行。

参考代码(数学法)

cpp

#include <iostream>

using namespace std;

int main()

{

int n;

cin >> n;

int ans = 0;

for(int i = 1; i <= n; i++)

{

int first = i % 10; // 取个位作为基准

bool flag = true;

int tmp = i;

while(tmp != 0)

{

int digit = tmp % 10;

if(digit != first) // 有一个位和基准(个位数)不同

{

flag = false; // flag改变

break;

}

tmp /= 10;

}

if(flag)

ans++;

}

cout << ans << endl;

return 0;

}

参考代码(字符串法)

cpp

#include <iostream>

#include <string>

using namespace std;

int main()

{

int n;

cin >> n;

int ans = 0;

for(int i = 1; i <= n; i++)

{

string s = to_string(i);

bool ok = true;

for(int j = 1; j < s.size(); j++)

{

if(s[j] != s[0]) // 直接每一位都和首位比较

{

ok = false;

break;

}

}

if(ok)

ans++;

}

cout << ans << endl;

return 0;

}

---

例题3:B3841 [GESP202306 二级] 自幂数判断

题目描述

自幂数是指一个 N 位数,满足各位数字 N 次方之和等于本身。

输入 M 个正整数,依次判断每个数是否为自幂数,是则输出 T,否则输出 F。

输入格式

第一行是正整数 M。

接下来 M 行,每行一个待判断的正整数。

输出格式

输出 M 行,对应每个数的判断结果。

解题思路

先获取数字的总位数,再逐位计算对应位数的次方并累加,最终比较累加和与原数是否相等,使用 pow 函数计算幂次。

要是数学法,整个过程会比较复杂,字符串法则比较方便,取到len就是数字位数。

参考代码(数学法)

cpp

#include <iostream>

#include <cmath>

using namespace std;

int main()

{

int n;

cin >> n;

for(int i = 1; i <= n; i++)

{

int a;

cin >> a;

// 第一步:统计位数

int len = 0;

int tmp = a;

while(tmp != 0)

{

len++;

tmp /= 10; // 其实就是看tmp除以几个10能变成0

}

// 第二步:计算各位len次方之和

int sum = 0;

tmp = a;

while(tmp != 0)

{

int d = tmp % 10;

sum += pow(d, len); // 每一位的位数次方之和

tmp /= 10;

}

// 第三步:判定结果

if(sum == a)

cout << "T" << endl;

else

cout << "F" << endl;

}

return 0;

}

参考代码(字符串法)

cpp

#include <iostream>

#include <string>

#include <cmath>

using namespace std;

int main()

{

int n;

cin >> n;

for(int i = 1; i <= n; i++)

{

int a;

cin >> a;

string s = to_string(a);

int len = s.size(); // 长度len就是n的位数

int sum = 0;

for(int j = 0; j < len; j++)

{

int d = s[j] - '0';

sum += pow(d, len); // 每一位的位数次方之和

}

if(sum == a)

cout << "T" << endl;

else

cout << "F" << endl;

}

return 0;

}

---

例题4:B4065 [GESP202412 二级] 数位和

题目描述

给定 n 个正整数,求这些正整数的数位和中的最大值。

输入格式

第一行包含一个正整数 n。

之后 n 行,每行包含一个正整数。

输出格式

输出这些正整数的数位和的最大值。

解题思路

对每个数计算数位和,同时维护最大值变量,每次计算完成后比较更新。

参考代码(数学法)

cpp

#include <iostream>

using namespace std;

int main()

{

int n;

cin >> n;

int max_sum = 0;

for(int i = 1; i <= n; i++)

{

int x;

cin >> x;

int sum = 0;

int tmp = x; // 备份要拆的数

while(tmp != 0) // 当还没变成0的时候

{

int digit = tmp % 10; // 取出当前个位数字

sum += digit; // 数位之和

tmp /= 10; // 去掉个位数

}

if(sum > max_sum)

max_sum = sum;

}

cout << max_sum << endl;

return 0;

}

参考代码(字符串法)

cpp

#include <iostream>

#include <string>

using namespace std;

int main()

{

int n;

cin >> n;

int max_sum = 0;

for(int i = 1; i <= n; i++)

{

string s;

cin >> s;

int sum = 0;

for(int j = 0; j < s.size(); j++)

{

sum += s[j] - '0'; // 数位之和

}

if(sum > max_sum)

max_sum = sum;

}

cout << max_sum << endl;

return 0;

}

---

例题5:B4036 [GESP202409 二级] 数位之和

题目描述

一个正整数是美丽数字,当且仅当它的各位数字总和是 7 的倍数。

给定 n 个正整数,依次判断每个数是否为美丽数字,是则输出 Yes,否则输出 No。

输入格式

第一行包含一个正整数 n。

之后 n 行,每行一个正整数。

输出格式

每个正整数对应输出一行结果。

参考代码(数学法)

cpp

#include <iostream>

using namespace std;

int main()

{

int n;

cin >> n;

for(int i = 1; i <= n; i++)

{

int x;

cin >> x;

int sum = 0;

int tmp = x; // 备份要拆的数

while(tmp != 0) // 当还没变成0的时候

{

int digit = tmp % 10; // 取出当前个位数字

sum += digit; // 求数位和

tmp /= 10; // 去掉个位数

}

if(sum % 7 == 0)

cout << "Yes" << endl;

else

cout << "No" << endl;

}

return 0;

}

参考代码(字符串法)

cpp

#include <iostream>

#include <string>

using namespace std;

int main()

{

int n;

cin >> n;

for(int i = 1; i <= n; i++)

{

string s;

cin >> s;

int sum = 0;

for(int j = 0; j < s.size(); j++)

{

sum += s[j] - '0';

}

if(sum % 7 == 0)

cout << "Yes" << endl;

else

cout << "No" << endl;

}

return 0;

}

---

例题6:B4007 [GESP202406 二级] 计数

题目描述

给定正整数 n 和幸运数 k(1≤k≤9),求从 1 到 n 的所有正整数中,数字 k 一共出现了多少次。

输入格式

第一行包含一个正整数 n。

第二行包含一个正整数 k。

输出格式

输出数字 k 出现的总次数。

解题思路

外层循环枚举 1 到 n 的所有整数,内层逐位判断当前位是否等于 k,相等则总计数加一。

参考代码(数学法)

cpp

#include <iostream>

using namespace std;

int main()

{

int n, k;

cin >> n >> k;

int cnt = 0;

for(int i = 1; i <= n; i++)

{

int tmp = i; // 备份要拆的数

while(tmp != 0) // 当还没变成0的时候

{

int digit = tmp % 10; // 取出当前个位数字

if(digit == k) // 统计各个位上k的数量

cnt++;

tmp /= 10; // 去掉个位数

}

}

cout << cnt << endl;

return 0;

}

参考代码(字符串法)

cpp

#include <iostream>

#include <string>

using namespace std;

int main()

{

int n, k;

cin >> n >> k;

int cnt = 0;

for(int i = 1; i <= n; i++)

{

string s = to_string(i);

for(int j = 0; j < s.size(); j++)

{

int digit = s[j] - '0'; // 注意减去字符0转普通整数

if(digit == k) // 不然比如'3'和3无法判相等

cnt++;

}

}

cout << cnt << endl;

return 0;

}

---

三、解题总结与避坑

1. 方法选择原则:学过字符串的话优先处理选字符串法。

2. 原数保护原则:数学法处理时,用循环临时变量拆解数字,保留原数用于最终判定。

3. 字符转数规范:字符串法中 s[i] - '0' 是常用写法,特别是求和、或者和整数做比较运算之类的,要注意你直接遍历到的s[i]是字符1,字符2,字符3……不是常规数字。

[1]: https://depth52.top/usr/uploads/2026/06/263579590.png