完整教程:David自习刷题室——【蓝桥杯刷题备战】乘法表

在这里插入图片描述在这里插入图片描述

点击下面查看作者专栏
C语言专栏
编程百度
如何获取自己的代码仓库

一、题目来源与图片

Lucy的空间骇客裂缝:洛谷九九乘法表

在这里插入图片描述



二、题目分析与做题思路

2.1)题目分析

像我们平时所熟知的九九乘法表在编程中指的是十进制的九九乘法表
Lucy的空间骇客裂缝:不同格式的九九乘法表


题目中所要求的乘法表
核心的区别在于: 这个乘法表是基于一个给定的进制P来构建和显示的

  • 标准九九乘法表: 在十进制下,乘数从 19
    每一行i包含的表达式从i × 1i × i
  • P 进制乘法表: 在这个问题中,乘数从1 P-1
    表格的结构遵循相同的逻辑:共有 P-1 行,第 i 行包含 i 个表达式,分别是 i × 1, i × 2, …, i × i
  • P 进制表示
    数字字符集: 对于 P<=10 的情况,使用数字 0-9。对于 P > 10 的情况,大于等于 10 的数值需要用大写字母表示:10A, 11B, ..., 35Z
    因此,完整的字符集是 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ

    乘积 i × j i \times j i×j:乘积的结果可能会大于或等于 P
    因此在 P P P 进制下可能是一个多位数。例如,在 4 进制下计算 2x2=4
    数值 44 进制中表示为 10_4(即 1 × 4 1 + 0 × 4 0 1 \times 4^1 + 0 \times 4^0 1×41+0×40),这是一个两位数

  • 输出格式要求

表达式格式: 每个乘法表达式的格式必须是AxB=C,其中A、B、C分别是乘数 1、乘数 2 和乘积的 P 进制表示

乘数顺序: 表达式中两个数相乘的顺序必须是行号x列好=号

分隔符: 相邻的两个表达式之间用一个空格分隔

换行: 每一行输出完毕后需要换行



2.2)思路解析

样例深度分析

样例 1: P = 4 P=4 P=4(四进制)

  • 目标: 生成一个4进制的乘法表,共有 4 - 1 = 3
  • 第 1 行 ( i = 1 i=1 i=1):
    • j j j 的范围是 1 到 1
    • j = 1 j=1 j=1: 计算 1 × 1 = 1 1 \times 1 = 1 1×1=1
      14进制下是1
      表达式为1*1=1
  • 第 2 行 i = 2 i=2 i=2
    • j j j 的范围是 1 到 2

    • j = 1 j=1 j=1: 计算 2 × 1 = 2 2 \times 1 = 2 2×1=2
      24进制下是 2
      表达式为 2*1=2
      我们需要将 4 转换为 4 进制
      表达式为2*2=10

    • j = 2 j=2 j=2: 计算 2 × 2 = 4 2 \times 2 = 4 2×2=4

  • 第 3 行 ( i = 3 i=3 i=3)
    • j j j 的范围是 1 到 3
    • j = 1 j=1 j=1: 计算 3 × 1 = 3 3 \times 1 = 3 3×1=3
      34 进制下是 3
      表达式为 3*1=3
    • j = 2 j=2 j=2: 计算 3 × 2 = 6 3 \times 2 = 6 3×2=6
      6 转换为 4 进制: 6 = 1 × 4 + 2 6 = 1 \times 4 + 2 6=1×4+2
      表达式为 3*2=12
    • j = 3 j=3 j=3: 计算 3 × 3 = 9 3 \times 3 = 9 3×3=9
      9 转换为 4进制: 9 = 2 × 4 + 1 9 = 2 \times 4 + 1 9=2×4+1
      表达式为 3*3=21

样例 2: P = 8 P=8 P=8(八进制)
逻辑与上面相同

  • 最后一行是第 7 行 ( i = 7 i=7 i=7)
    该表达式为 7*7=61


代码算法设计思路

  • 解题的关键点
    1. 理解双重循环结构:外层循环控制行数(1 到 P − 1 P-1 P1),内层循环控制每行的表达式个数(1 到当前行号)
    2. 掌握任意进制转换:核心是能够将一个十进制数转换为 2 2 2 36 36 36 之间任意进制的字符串表示。这需要用到取模 (%) 和整除 (// 或 /) 操作,以及一个包含 ‘0’-‘9’ 和 ‘A’-‘Z’ 的字符映射表
    3. 严格遵守输出格式:包括乘数的顺序、乘积的进制表示、表达式之间的空格分隔以及行末无空格的要求
    4. 处理特殊情况:注意 P > 10 P > 10 P>10 时,数值 10-35 要正确映射到字母 A-Z。题目给定的 P P P 范围是 2 ≤ P ≤ 36 2 \le P \le 36 2P36,解题方案必须能覆盖这个范围内的所有情况


字符映射表
const char DIGITS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";


“除基取余”法递归实现进制转换打印

Lucy的空间骇客裂缝:进制转换思路

// 递归函数:将十进制数 num 转换为 P 进制并打印
// 递归的特性自然地实现了“除基取余”后的逆序输出
void printBaseP(int num, int P) {
if (num >= P) {
// 如果 num >= P,说明还有更高的位,先递归处理更高位
printBaseP(num / P, P);
}
// 打印当前最低位的字符。这一步在递归返回时执行,保证了输出顺序的正确性。
putchar(DIGITS[num % P]);
}
  • 我们以十进制转成二进制为例:
    在这里插入图片描述

  • 10进制转成4进制也是同样的道理
    在这里插入图片描述



主函数

最常规的九九乘法表打印只不过添加了一些函数调用

int main() {
int P;
// 读取进制数 P,如果读取失败则直接退出
if (scanf("%d", &P) != 1) return 1;
// 外层循环控制行,i 从 1 到 P-1
for (int i = 1; i < P; i++) {
// 内层循环控制列,j 从 1 到 i
for (int j = 1; j <= i; j++) {
// 打印乘数 i 的 P 进制字符(i < P,必为一位数)
putchar(DIGITS[i]);
putchar('*');
// 打印乘数 j 的 P 进制字符(j < P,必为一位数)
putchar(DIGITS[j]);
putchar('=');
// 递归打印乘积 i*j 的 P 进制字符串
printBaseP(i * j, P);
// 如果不是当前行的最后一个表达式,打印一个空格分隔
if (j < i) putchar(' ');
}
// 一行结束,打印换行符
putchar('\n');
}
return 0;
}


三、完整代码

#include <stdio.h>
  // 字符集,涵盖 0-9 和 A-Z,用于 2-36 进制的表示
  const char DIGITS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  // 递归函数:将十进制数 num 转换为 P 进制并打印
  // 递归的特性自然地实现了“除基取余”后的逆序输出
  void printBaseP(int num, int P) {
  if (num >= P) {
  // 如果 num >= P,说明还有更高的位,先递归处理更高位
  printBaseP(num / P, P);
  }
  // 打印当前最低位的字符。这一步在递归返回时执行,保证了输出顺序的正确性。
  putchar(DIGITS[num % P]);
  }
  int main() {
  int P;
  // 读取进制数 P,如果读取失败则直接退出
  if (scanf("%d", &P) != 1) return 1;
  // 外层循环控制行,i 从 1 到 P-1
  for (int i = 1; i < P; i++) {
  // 内层循环控制列,j 从 1 到 i
  for (int j = 1; j <= i; j++) {
  // 打印乘数 i 的 P 进制字符(i < P,必为一位数)
  putchar(DIGITS[i]);
  putchar('*');
  // 打印乘数 j 的 P 进制字符(j < P,必为一位数)
  putchar(DIGITS[j]);
  putchar('=');
  // 递归打印乘积 i*j 的 P 进制字符串
  printBaseP(i * j, P);
  // 如果不是当前行的最后一个表达式,打印一个空格分隔
  if (j < i) putchar(' ');
  }
  // 一行结束,打印换行符
  putchar('\n');
  }
  return 0;
  }


四、优化版本

算法题目达到结果即可,效率系统优化仅作了解

#include <stdio.h>
  #include <stdlib.h>
    /* 用于表示 P 进制数字的字符集。
    涵盖了 0-9 和 A-Z,足以应对题目中最高 36 进制的情况。
    DIGITS[0] 是 '0', DIGITS[10] 是 'A', DIGITS[35] 是 'Z'。
    */
    const char DIGITS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    /**
    * 功能:将一个十进制非负整数 num 转换为 P 进制的字符串表示。
    *
    * 参数:
    * - num: 待转换的十进制非负整数。
    * - P: 目标进制(2 <= P <= 36)。
    * - result: 用于存储转换结果的字符数组缓冲区。调用者需确保其大小足够。
    * 对于 32 位整数,最大值为 2^31-1,在 2 进制下约有 31 位,加上结束符,35 字节足够。
    */
    void toBaseP(int num, int P, char *result) {
    // 特殊情况处理:如果 num 为 0,直接返回字符串 "0"
    if (num == 0) {
    result[0] = '0';
    result[1] = '\0';
    return;
    }
    // 临时缓冲区,用于存储逆序的 P 进制位
    // 35 个字节足够存储 int 类型在 2 进制下的所有位
    char temp[35];
    int temp_len = 0;
    // 核心算法:除基取余法
    while (num > 0) {
    // 取余数,得到当前最低位的数值,并映射为字符
    temp[temp_len++] = DIGITS[num % P];
    // num 除以 P,去掉已经处理的最低位
    num /= P;
    }
    // 将 temp 中的字符逆序复制到 result 中,得到正确的顺序
    for (int i = 0; i < temp_len; i++) {
    // result[0] 对应 temp 的最后一个元素,即最高位
    result[i] = temp[temp_len - 1 - i];
    }
    // 在字符串末尾添加空字符 '\0',使其成为一个合法的 C 字符串
    result[temp_len] = '\0';
    }
    /**
    * 主函数:程序的入口点。
    * 负责读取输入、控制循环结构、调用进制转换函数以及格式化输出。
    */
    int main() {
    int P;
    // 1. 读取输入:从标准输入读取一个整数 P
    // scanf 返回成功读取的项目数,如果不是 1,说明输入格式错误
    if (scanf("%d", &P) != 1) {
    // 可以选择打印错误信息或直接退出
    return 1;
    }
    // 根据题目要求,乘法表有 P-1 行。
    // 外层循环控制行号 i,从 1 到 P-1。
    for (int i = 1; i < P; i++) {
    // 内层循环控制每一行中的表达式。
    // 第 i 行有 i 个表达式,列号 j 从 1 到 i。
    for (int j = 1; j <= i; j++) {
    // a. 计算十进制下的乘积
    int prod = i * j;
    // b. 获取乘数 i 和 j 的 P 进制字符表示。
    // 由于循环条件保证了 i < P 且 j < P,它们在 P 进制下必定是单一位的数字。
    // 因此,我们可以直接通过 DIGITS 数组进行映射,无需调用通用的 toBaseP 函数。
    // 这样做既简单又高效。
    char s_i = DIGITS[i];
    char s_j = DIGITS[j];
    // c. 获取乘积 prod 的 P 进制字符串表示。
    // 乘积可能会大于等于 P,是一个多位数,所以必须调用通用的转换函数。
    char s_prod[35]; // 声明一个足够大的缓冲区
    toBaseP(prod, P, s_prod);
    // d. 按照题目要求的格式 "A*B=C" 打印表达式。
    // 注意乘数的顺序是 i * j。
    printf("%c*%c=%s", s_i, s_j, s_prod);
    // e. 输出格式控制:在同一行的表达式之间打印一个空格。
    // 如果当前不是这一行的最后一个表达式 (j < i),则打印空格。
    // 这样可以保证行末不会有多余的空格。
    if (j < i) {
    printf(" ");
    }
    }
    // 当内层循环结束,说明一行打印完毕,输出一个换行符。
    printf("\n");
    }
    return 0; // 程序正常结束
    }

希望读者多多三连

给小编一些动力

蟹蟹啦!

在这里插入图片描述

posted @ 2026-01-01 09:24  yangykaifa  阅读(1)  评论(0)    收藏  举报