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 标准语法编译代码

image.png

适用场景

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

标准代码模板

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 的优美正整数的数量。

解题思路

枚举 1n 的所有整数,逐位判断是否所有数位都与基准数字相同,全部相同则计数加一。
用数学法相对会有点难度,需要先拆一位作为基准数,然后拆其他数位做判断。
用字符串法则比较好考虑,拿每一位和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;
}

三、解题总结与避坑

  1. 方法选择原则:学过字符串的话优先处理选字符串法。
  2. 原数保护原则:数学法处理时,用循环临时变量拆解数字,保留原数用于最终判定。
  3. 字符转数规范:字符串法中 s[i] - '0' 是常用写法,特别是求和、或者和整数做比较运算之类的,要注意你直接遍历到的s[i]是字符1,字符2,字符3……不是常规数字。

标签: none

评论已关闭