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


索引与导读
一、题目来源与图片

二、题目分析与做题思路
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 × j i \times j i×j:乘积的结果可能会大于或等于P
因此在 P P P 进制下可能是一个多位数。例如,在 4 进制下计算2x2=4
数值4在4进制中表示为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
1在4进制下是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
2在4进制下是2
表达式为2*1=2
我们需要将4转换为4进制
表达式为2*2=10j = 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
3在4进制下是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 到 P − 1 P-1 P−1),内层循环控制每行的表达式个数(1 到当前行号)
- 掌握任意进制转换:核心是能够将一个十进制数转换为 2 2 2 到 36 36 36 之间任意进制的字符串表示。这需要用到取模 (%) 和整除 (// 或 /) 操作,以及一个包含 ‘0’-‘9’ 和 ‘A’-‘Z’ 的字符映射表
- 严格遵守输出格式:包括乘数的顺序、乘积的进制表示、表达式之间的空格分隔以及行末无空格的要求
- 处理特殊情况:注意 P > 10 P > 10 P>10 时,数值 10-35 要正确映射到字母 A-Z。题目给定的 P P P 范围是 2 ≤ P ≤ 36 2 \le P \le 36 2≤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-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; // 程序正常结束
}
希望读者多多三连
给小编一些动力
蟹蟹啦!


浙公网安备 33010602011771号