

点击下面查看作者专栏 🔥🔥C语言专栏🔥🔥 🌊🌊编程百度🌊🌊 🌠🌠如何获取自己的代码仓库🌠🌠
索引与导读
一、题目来源与图片

二、题目分析与做题思路
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=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
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,解题方案必须能覆盖这个范围内的所有情况
字符映射表
c
const char DIGITS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
"除基取余"法递归实现进制转换打印
c
// 递归函数:将十进制数 num 转换为 P 进制并打印
// 递归的特性自然地实现了"除基取余"后的逆序输出
void printBaseP(int num, int P) {
if (num >= P) {
// 如果 num >= P,说明还有更高的位,先递归处理更高位
printBaseP(num / P, P);
}
// 打印当前最低位的字符。这一步在递归返回时执行,保证了输出顺序的正确性。
putchar(DIGITS[num % P]);
}
-
我们以十进制转成二进制 为例:

-
10进制转成4进制也是同样的道理

主函数
最常规的九九乘法表打印 只不过添加了一些函数调用
c
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;
}
三、完整代码
c
#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;
}
四、优化版本
算法题目达到结果即可,效率系统优化仅作了解
c
#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; // 程序正常结束
}
希望读者多多三连
给小编一些动力
蟹蟹啦!
