GESP 二级编程题之——拆数字
GESP 二级编程题之——拆数字(数位处理)
拆数字(数位处理)是 GESP C++ 二级的必考编程题型,核心是对整数的每一位数字进行提取、统计、计算或特征判定。常见考点覆盖数位求和、数位计数、特征数位筛选、自幂数判断等,有两种成熟的通用实现方案。
一、拆数字核心模板
所有数位处理题都可以套用以下两种固定模板,学过字符串的同学强烈建议用字符串。
模板1:数学法(取模/整除)
实现原理
通过循环逐次%10提取当前个位进行处理,然后/10去掉个位数字,循环终止条件为数字被缩减为 0。
标准代码模板
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 标准语法编译代码

适用场景
需要按高位到低位顺序处理、判断数位位置、统计特定数字出现次数的题目,代码直观不易出错。
标准代码模板
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 次则答案计数加一。
参考代码(数学法)
#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;
}参考代码(字符串法)
#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],即第一位比较就行。
参考代码(数学法)
#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;
}参考代码(字符串法)
#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就是数字位数。
参考代码(数学法)
#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;
}参考代码(字符串法)
#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 行,每行包含一个正整数。
输出格式
输出这些正整数的数位和的最大值。
解题思路
对每个数计算数位和,同时维护最大值变量,每次计算完成后比较更新。
参考代码(数学法)
#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;
}参考代码(字符串法)
#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 行,每行一个正整数。
输出格式
每个正整数对应输出一行结果。
参考代码(数学法)
#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;
}参考代码(字符串法)
#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,相等则总计数加一。
参考代码(数学法)
#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;
}参考代码(字符串法)
#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;
}三、解题总结与避坑
- 方法选择原则:学过字符串的话优先处理选字符串法。
- 原数保护原则:数学法处理时,用循环临时变量拆解数字,保留原数用于最终判定。
- 字符转数规范:字符串法中
s[i] - '0'是常用写法,特别是求和、或者和整数做比较运算之类的,要注意你直接遍历到的s[i]是字符1,字符2,字符3……不是常规数字。
评论已关闭