一、题目核心信息
1. 题目描述
将 M 进制数 X 转换为 N 进制数输出(2≤M,N≤36);输入字母为大写,输出字母为小写。
2. 输入输出格式
- 输入:第一行是进制 M、N;第二行是 M 进制数 X(字符串形式,支持数字+大写字母)。
- 输出:X 的 N 进制表示(字母小写)。
3. 样例
输入:10 2 + 11 → 输出:1011(十进制11转二进制1011)。
二、初始代码思路:十进制中转法
1. 核心逻辑(两步走)
以十进制为中间桥梁,避免直接转换 M→N 的复杂度:
M进制字符串X
转十进制数
转N进制字符串
输出(小写)
2. 初始代码逐段解析
c
#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main()
{
int m,n;
char x[101];
scanf("%d %d",&m,&n);
scanf("%s",x); // 读取M进制字符串(兼容字母)
// 步骤1:M进制 → 十进制(unsigned long long防溢出)
unsigned long long decimal=0; // 关键:不用int!
int len=strlen(x);
for(int i=0;i<len;i++)
{
char c=x[i];
int val=0;
// 字符转数值:数字(0-9)→0-9,大写字母(A-Z)→10-35
if(c>='0'&&c<='9') val=c-'0';
else val=c-'A'+10;
// 核心公式:逐位累加(位权=进制^位数)
decimal=decimal*m+val;
}
// 步骤2:十进制 → N进制(除基取余法,余数逆序)
char result[100];
int idx=0;
// 特殊处理:十进制为0的情况(避免结果为空)
if(decimal==0) result[idx++]='0';
else
{
while(decimal>0)
{
int remain=decimal%n; // 取余数(N进制的一位)
// 余数转字符:0-9→数字,10-35→小写字母
if(remain<10) result[idx++]=remain+'0';
else result[idx++]=remain-10+'a';
decimal/=n; // 整除N,缩小数值
}
}
// 反转结果(取余得到的是逆序,需转正)
for(int i=0;i<idx/2;i++)
{
char temp=result[idx-1-i];
result[idx-1-i]=result[i];
result[i]=temp;
}
result[idx]='\0'; // 字符串结束符
printf("%s",result);
return 0;
}
三、初始代码的核心问题与局限
1. 最大问题:数值溢出(为什么不能用int?)
int通常是4字节,范围仅-2147483648 ~ 2147483647(约21亿);- 若 M 进制数较长(如36进制的8位
ZZZZZZZZ,十进制值≈2.8万亿),int会溢出,数值"绕回"导致结果错误; - 代码中用
unsigned long long(8字节,范围0~1800亿亿)可缓解,但仍无法处理超长字符串(如36进制15位以上)。
2. 其他局限
- 输入缓冲区固定为101字节,超长输入会溢出;
- 无输入验证(如 M=16 时输入 'G' 会计算错误);
- 十进制中转法本质依赖数值类型,无法处理任意长度的大数。
四、核心知识点总结
1. 进制转换核心原理
- M进制→十进制:按权展开
decimal = decimal*M + 位值(从高位到低位); - 十进制→N进制:除基取余法(除以N取余数,逆序排列余数);
- 字符与数值转换:
- 大写字母→数值:
val = c-'A'+10(A=10,Z=35); - 数值→小写字母:
c = val-10+'a'(10=a,35=z)。
- 大写字母→数值:
2. 关键注意点
- 必须处理"输入为0"的边界情况(否则结果为空);
- 除基取余得到的余数是逆序,需反转后输出;
- 输入字母大写、输出小写是题目硬性要求。
五、优化方案:大数直接转换法(解决溢出问题)
1. 核心思路(放弃十进制中转)
直接对 M 进制字符串模拟"手工除法":不断除以 N,每次余数为 N 进制的一位,直到商为0(余数逆序即为结果)。
否
是
M进制字符串X
X是否为0?
X除以N,得到商(M进制)+余数(N进制一位)
余数存入结果
反转结果输出
2. 优化后核心函数(大数除法)
c
// 大数除法:M进制字符串 ÷ N,基数固定为M,返回余数,商存入quotient
int big_divide(const char *dividend, int N, int base, char *quotient) {
int remainder = 0;
char temp_quotient[1024] = {0};
int q_idx = 0;
int len = strlen(dividend);
for (int i = 0; i < len; i++) {
char c = dividend[i];
int digit = isdigit(c) ? (c-'0') : (c-'A'+10);
// 纯int计算(无溢出:remainder≤35 * base≤36 + digit≤35 = 1295 < int范围)
int current = remainder * base + digit;
int q_digit = current / N;
remainder = current % N;
// 商转字符(M进制,大写)
temp_quotient[q_idx++] = (q_digit<10) ? (q_digit+'0') : (q_digit-10+'A');
}
temp_quotient[q_idx] = '\0';
// 移除商的前导零
remove_leading_zeros(temp_quotient, quotient);
return remainder;
}
六、易错点与避坑指南
- 数据类型选择 :十进制中转法优先用
unsigned long long,大数场景必须用字符串模拟; - 前导零处理 :除法得到的商需移除前导零,避免出现
00123这类无效字符串; - 输入安全性 :用
scanf("%1023s", x)限制输入长度,避免缓冲区溢出; - 进制基数一致性:大数除法中,被除数始终是 M 进制,因此基数固定为 M(核心易错点)。
七、最终结论
- 十进制中转法:思路简单,适合小数据场景,机试基础分必拿;
- 大数直接转换法:无溢出风险,能处理任意长度输入,是机试满分方案;
- 核心原则:进制转换的本质是"位权运算",无论哪种方法,都要保证字符-数值转换、基数计算的正确性。