

索引与导读
- 一、题目来源与图片
- 二、题目分析与做题思路
- 2.1)题目分析
- 2.2)思路解析
- 样例深度分析
- 代码算法设计思路
- 字符映射表
- “除基取余”法递归实现进制转换打印
- 主函数
- 三、完整代码
- 四、优化版本
- 希望读者多多三连
- 给小编一些动力
- 蟹蟹啦!
一、题目来源与图片

二、题目分析与做题思路
2.1)题目分析
像我们平时所熟知的九九乘法表在编程中指的是
十进制的九九乘法表
Lucy的空间骇客裂缝:不同格式的九九乘法表
题目中所要求的乘法表
核心的区别在于: 这个乘法表是基于一个给定的进制P来构建和显示的
- 标准九九乘法表: 在十进制下,乘数从
1到9
每一行i包含的表达式从i × 1到i × i- P 进制乘法表: 在这个问题中,乘数从
1到P-1
表格的结构遵循相同的逻辑:共有P-1行,第i行包含i个表达式,分别是i × 1, i × 2, …, i × i
P进制表示
数字字符集: 对于P<=10的情况,使用数字0-9。对于P > 10的情况,大于等于10的数值需要用大写字母表示:10用A,11用B,...,35用Z
因此,完整的字符集是0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
乘积 i×ji \times ji×j:乘积的结果可能会大于或等于P
因此在 PPP 进制下可能是一个多位数。例如,在 4 进制下计算2x2=4
数值4在4进制中表示为10_4(即 1×41+0×401 \times 4^1 + 0 \times 4^01×41+0×40),这是一个两位数输出格式要求
表达式格式: 每个乘法表达式的格式必须是AxB=C,其中A、B、C分别是乘数 1、乘数 2 和乘积的 P 进制表示
乘数顺序: 表达式中两个数相乘的顺序必须是行号x列好=号
分隔符: 相邻的两个表达式之间用一个空格分隔
换行: 每一行输出完毕后需要换行
2.2)思路解析
样例深度分析
样例 1:P=4P=4P=4(四进制)
- 目标: 生成一个
4进制的乘法表,共有4 - 1 = 3行 - 第 1 行 (i=1i=1i=1):
- jjj 的范围是 1 到 1
- j=1j=1j=1: 计算 1×1=11 \times 1 = 11×1=1
1在4进制下是1
表达式为1*1=1
- 第 2 行(i=2i=2i=2)
jjj 的范围是 1 到 2
j=1j=1j=1: 计算 2×1=22 \times 1 = 22×1=2
2在4进制下是2
表达式为2*1=2
我们需要将4转换为4进制
表达式为2*2=10j=2j=2j=2: 计算 2×2=42 \times 2 = 42×2=4
- 第 3 行 (i=3i=3i=3)
- jjj 的范围是 1 到 3
- j=1j=1j=1: 计算 3×1=33 \times 1 = 33×1=3
3在4进制下是3
表达式为3*1=3 - j=2j=2j=2: 计算 3×2=63 \times 2 = 63×2=6
将6转换为4进制:6=1×4+26 = 1 \times 4 + 26=1×4+2
表达式为3*2=12 - j=3j=3j=3: 计算 3×3=93 \times 3 = 93×3=9
将9转换为4进制:9=2×4+19 = 2 \times 4 + 19=2×4+1
表达式为3*3=21
样例 2:P=8P=8P=8(八进制)
逻辑与上面相同
- 最后一行是第
7行 (i=7i=7i=7)
该表达式为7*7=61
代码算法设计思路
- 解题的关键点
- 理解双重循环结构:外层循环控制行数(1 到 P−1P-1P−1),内层循环控制每行的表达式个数(1 到当前行号)
- 掌握任意进制转换:核心是能够将一个十进制数转换为 222 到 363636 之间任意进制的字符串表示。这需要用到取模 (%) 和整除 (// 或 /) 操作,以及一个包含 ‘0’-‘9’ 和 ‘A’-‘Z’ 的字符映射表
- 严格遵守输出格式:包括乘数的顺序、乘积的进制表示、表达式之间的空格分隔以及行末无空格的要求
- 处理特殊情况:注意 P>10P > 10P>10 时,数值 10-35 要正确映射到字母 A-Z。题目给定的 PPP 范围是 2≤P≤362 \le P \le 362≤P≤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]);
}
我们以十进制转成二进制为例:

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-1for (int i = 1; i < P; i++) {// 内层循环控制列,j 从 1 到 ifor (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; // 程序正常结束}
希望读者多多三连
给小编一些动力
蟹蟹啦!
