1 条题解

  • 0
    @ 2025-6-16 15:44:57

    题解:Basically Speaking(P1546)

    一、题目分析

    本题要求实现数制转换程序,核心任务是将一个数从任意进制(2-16)转换为另一种进制(2-16),并按7位右对齐格式输出。关键要点包括:

    • 输入数可能包含数字0-9和字母A-F(对应10-15)
    • 转换后结果长度超过7位时输出"ERROR"
    • 结果需右对齐,左侧补空格至7位

    二、代码解析(C语言实现)

    #include <stdio.h>
    #include <algorithm>
    #include <string.h>
    using namespace std;
    
    const int maxn = 1e5 + 5;
    char a[maxn];  // 存储输入的原进制数
    char b[maxn];  // 存储转换后的目标进制数
    
    // 字符转数字(A-F对应10-15,0-9对应0-9)
    int change(char c) {
        if (c == 'A') return 10;
        else if (c == 'B') return 11;
        else if (c == 'C') return 12;
        else if (c == 'D') return 13;
        else if (c == 'E') return 14;
        else if (c == 'F') return 15;
        else return c - '0';
    }
    
    // 数字转字符(10-15对应A-F,0-9对应0-9)
    int change1(int n) {
        if (n == 10) return 'A';
        else if (n == 11) return 'B';
        else if (n == 12) return 'C';
        else if (n == 13) return 'D';
        else if (n == 14) return 'E';
        else if (n == 15) return 'F';
        else return n + '0';
    }
    
    // 计算n的len次方(用于原进制转十进制)
    int pow(int n, int len) {
        int sum = 1;
        for (int i = 1; i <= len; i++) {
            sum *= n;
        }
        return sum;
    }
    
    int main() {
        int basc1, basc2;  // 原进制和目标进制
        
        while (scanf("%s", a) != EOF) {
            scanf("%d %d", &basc1, &basc2);
            int len = strlen(a);
            int sum = 0;
            
            // 原进制转十进制(核心步骤)
            for (int i = 0; i < len; i++) {
                sum += change(a[i]) * pow(basc1, len - 1 - i);
            }
            
            // 十进制转目标进制(除基取余法)
            int r, k = 0;
            while (sum > 0) {
                r = sum % basc2;
                b[k++] = change1(r);
                sum /= basc2;
            }
            
            // 结果长度检查
            if (k > 7) {
                printf("  ERROR\n");
                continue;
            }
            
            // 右对齐输出:先补空格,再输出结果(逆序)
            for (int i = 1; i <= 7 - k; i++) {
                printf(" ");
            }
            for (int i = k - 1; i >= 0; i--) {
                printf("%c", b[i]);
            }
            printf("\n");
        }
        return 0;
    }
    

    三、关键步骤解析

    1. 原进制转十进制
      采用按位展开法:
      对于原进制数 ( N = d_0d_1d_2\cdots d_{n-1} ),其十进制值为:
      [ N_{10} = \sum_{i=0}^{n-1} d_i \times \text{basc1}^{n-1-i} ]
      例如,二进制数1111000的计算过程:
      [ 1 \times 2^6 + 1 \times 2^5 + 1 \times 2^4 + 1 \times 2^3 + 0 \times 2^2 + 0 \times 2^1 + 0 \times 2^0 = 120 ]
      代码中通过change(a[i])change(a[i])`将字符转换为对应数值,再乘以权值累加。

    2. 十进制转目标进制
      采用除基取余法:
      不断将十进制数除以目标进制基数basc2basc2,记录余数,直到商为0。余数逆序排列即为目标进制数。
      例如,十进制120转十六进制:
      [ 120 \div 16 = 7 \text{ 余 } 8 \quad \Rightarrow \quad 78_{16} ]
      代码中通过sumsum % basc2取余,change1(r)change1(r)将余数转换为对应字符。

    3. 右对齐处理

      • 若结果长度k>7k > 7,输出"ERROR"(固定7字符,左补2空格)
      • 否则,先输出7k7 - k个空格,再逆序输出结果(因取余顺序与实际数位顺序相反)

    四、易错点与优化

    1. 数值范围问题
      代码中使用intint类型存储中间结果,但当原进制数较大时(如16进制的ABCD),intint可能溢出。实际应使用longlonglong long或大数处理,但本题样例未超出intint范围(2^31-1=2147483647)。
    2. 输入合法性检查
      代码未验证输入数是否合法(如八进制数包含8-9),实际应添加校验:若某字符对应的数值≥原进制基数,则输入无效。

    五、样例验证

    以输入行123124212312 4 2为例:

    1. 原进制转十进制
      四进制数12312的各位权值计算:
      [ 1 \times 4^4 + 2 \times 4^3 + 3 \times 4^2 + 1 \times 4^1 + 2 \times 4^0 = 256 + 128 + 48 + 4 + 2 = 438 ]
    2. 十进制转二进制
      438 ÷ 2 = 219 余 0
      219 ÷ 2 = 109 余 1
      109 ÷ 2 = 54 余 1
      54 ÷ 2 = 27 余 0
      27 ÷ 2 = 13 余 1
      13 ÷ 2 = 6 余 1
      6 ÷ 2 = 3 余 0
      3 ÷ 2 = 1 余 1
      1 ÷ 2 = 0 余 1
      余数逆序为11011001101101100110(11位),长度超过7,输出"ERROR"。

    六、改进方向

    1. 大数处理:使用字符串存储中间结果,避免整数溢出
    2. 直接进制转换:跳过十进制中间步骤,直接进行进制转换(如二进制转十六进制可按4位分组)
    3. 合法性校验:添加输入数合法性检查,确保每个字符对应数值小于原进制基数
    • 1

    信息

    ID
    547
    时间
    1000ms
    内存
    256MiB
    难度
    3
    标签
    递交数
    3
    已通过
    1
    上传者