PAT 甲级题目讲解:1010《Radix》
✅ PAT 甲级题目讲解:1010《Radix》
📄 摘要:本文深度讲解了 PAT 甲级真题 1010《Radix》 的完整解题思路与 C++ 实现。题目核心在于进制转换函数的设计与二分查找优化。文中详细推导了未知进制的合法范围确定方法、单调性二分策略,以及如何利用
LLONG_MAX防止整数溢出,并总结了常见易错点与复杂度分析,适合准备 PAT/算法竞赛的同学参考学习。
🧩 题目简介
给定两个正整数 N1N1N1 和 N2N2N2,数字可能包含字母(0-9、a-z)表示。
现已知其中一个数的进制 radixradixradix,要求求出另一个数的进制 radixradixradix,使得两数相等,即 N1(r1)=N2(r2)N1(r1) = N2(r2)N1(r1)=N2(r2)。
若存在多个解,输出最小可能解;若无解,输出 Impossible。
🧪 样例分析
输入样例 1:
6 110 1 10
分析:
- 第三个输入
1表示第一个数 N1N1N1 的 radixradixradix 已知为10(即十进制的 6); - N2=110N2 = 110N2=110,若其 radix=2radix = 2radix=2,则对应值为 1×22+1×21+0=61\times2^2 + 1\times2^1 + 0 = 61×22+1×21+0=6,相等。
输出:
2
输入样例 2:
1 ab 1 1
分析:
- 1(2)=ab(r2)1(2) = ab(r2)1(2)=ab(r2) 求 r2r2r2,等式左边转十进制为 1;
- ababab 在最小合法进制(12)下值已远大于 1,该等式永远不可能成立。
输出:
Impossible
🔍 解题思路
📎 变量说明
| 变量名 | 类型 | 含义 |
|---|---|---|
a, b | string | 两个字符串表示的数字 |
t | int | tag,表示已知 radixradixradix是第几个数(1 或 2) |
rd | int | 已知数的 radixradixradix |
f(x, r) | function | 把字符串 x 以进制 r 转成十进制(如失败返回 -1) |
n1 | long long | 已知数转换为十进制的结果 |
k | int | 未知数中最大数位 |
l, r | long long | 二分查找区间 |
✅ Step 1:预处理统一格式
若 t == 2,说明第二个数的 radixradixradix 已知,交换 a 和 b,确保 a 是已知进制,统一转换成对 b 的进制求解:
cin >> a >> b >> t >> rd;
if(t == 2) swap(a, b);
✅ Step 2:将已知进制的 a 转换为十进制 n1
我们需要将字符串 a 从已知进制 rd 转换为十进制数值 n1,以便后续与另一个未知进制的数进行比较。
由于输入可能包含小写字母(即 a ~ z)与数字(即 0 ~ 9),我们要先将每一位字符映射为对应的十进制数值:
'0'~'9'对应 000 ~ 999;'a'~'z'对应 101010 ~ 353535;
✏️ 进制转换的数学推导
设字符串 x=d0d1d2…dk−1x = d_0d_1d_2\dots d_{k-1}x=d0d1d2…dk−1,在进制 rrr 下的十进制表示为:
val(x,r)=d0⋅rk−1+d1⋅rk−2+⋯+dk−1⋅r0 \text{val}(x, r) = d_0 \cdot r^{k-1} + d_1 \cdot r^{k-2} + \dots + d_{k-1} \cdot r^0 val(x,r)=d0⋅rk−1+d1⋅rk−2+⋯+dk−1⋅r0
在程序实现中,为避免计算幂次,采用左乘右加的迭代方式:
val=(((d0×r+d1)×r+d2)×r+⋯+dk−1)
\text{val} = (((d_0 \times r + d_1) \times r + d_2) \times r + \dots + d_{k-1})
val=(((d0×r+d1)×r+d2)×r+⋯+dk−1)
具体实现过程:
-
使用
long long s保存累加结果; -
每次将当前结果
s左移一位(即s * r),再加上当前数位值t; -
为防止溢出,判断乘加前是否可能超过
LLONG_MAX,防止不合法解误判为合法。
long long f(string x, int r){
long long s = 0;
for(int i = 0; i < x.size(); i++){
int t = (x[i] <= '9') ? (x[i] - '0') : (x[i] - 'a' + 10);
if(s > (LLONG_MAX - t) / r) return -1; // 溢出保护
s = s * r + t; // 将当前结果左移一位,再加上当前数位值 t
}
return s;
}
long long n1 = f(a, rd);
✅ Step 3:确定可能的 radix 区间
本题本质是通过二分枚举未知进制 radixradixradix,使得 f(b, radix) == n1。为了让二分有效进行,我们需要先确定可能的值 radixradixradix 域范围,即:最小可能 radixradixradix 和 最大可能 radixradixradix。
🧮 最小 radix(下界)
一个数字在某个进制下合法,要求其中每一位的数值 di<rd_i < rdi<r,即:
r>max{d0,d1,…,dk−1} r > \max\{d_0, d_1, \dots, d_{k-1}\} r>max{d0,d1,…,dk−1}
所以对于字符串 b,我们需找出其所有字符中最大的数位值 k,那么:
radixmin=k+1 \text radix_{min}= k + 1 radixmin=k+1
否则该进制下 b 中可能出现非法位。
🧮 最大 radix(上界)
我们需要寻找的 radixradixradix 满足:
f(b,radix)=f(a,rd)=n1 f(b, \text{radix}) = f(a, rd) = n_1 f(b,radix)=f(a,rd)=n1
设 n1n_1n1 是已知数 a 在其 radixradixradix rd 下转换为十进制的值。考虑:
- f(b,radix)f(b, radix)f(b,radix) 随 radixradixradix 增大而单调递增;
- 若 radixradixradix 太大,则
f(b, radix) > n1; - 所以最大 radixradixradix 的边界为 n1n_1n1。
换句话说:若 radix>n1radix > n_1radix>n1,最小可能得到的值也会超过 n1n_1n1,肯定不等于 n1n_1n1,无需再查。
🔁 实现代码如下:
要使 f(b, radix) == n1,则:
b中最大数位记为k,最小可能 radixradixradix 为k + 1;- 最大 radixradixradix 不会超过
n1:
int k = 0;
for(char ch : b){
int val = (ch <= '9') ? ch - '0' : ch - 'a' + 10;
k = max(k, val);
}
long long l = k + 1, r = max(l, n1); // 最小可能 radix,最大不超过 n1(确保区间合法)
✅ Step 4:二分查找使得 f(b, radix) == n1 的最小 radixradixradix
我们已获得 radix ∈ [k+1, n1] 的可能区间。
现在的任务是:在该区间内查找最小的 radixradixradix,使得 f(b, radix) == n1。
这是一个典型的“单调性 + 最值”问题,适合使用二分查找解决。
观察函数 f(b, radix) 的性质:
- radixradixradix 越大,
f(b, radix)的结果也越大(单调递增); - 一旦 radixradixradix 太大,
f()会超出 long long 范围或超过 n1n_1n1; - 所以我们可以对 radixradixradix 进行单调性剪枝,快速缩小范围;
🎯目标转化为“最小满足条件的 radix”
设:
- n1=f(a,rd)n_1 = f(a, rd)n1=f(a,rd) 为已知十进制目标值;
- 当前枚举 radixradixradix 为 midmidmid,计算 n2=f(b,mid)n_2 = f(b, mid)n2=f(b,mid):
判断分三种情况:
-
若 n2<n1n_2 < n_1n2<n1:说明 radixradixradix 偏小,应往右侧逼近:
l=mid+1 l = mid + 1 l=mid+1 -
若 n2>n1n_2 > n_1n2>n1 或 n2=−1n_2 = -1n2=−1(溢出):
r=mid−1 r = mid - 1 r=mid−1 -
若 n2=n1n_2 = n_1n2=n1:
- 说明找到一个合法 radixradixradix;
- 但要继续往左搜索更小合法解,直到收敛:
r=mid−1 r = mid - 1 r=mid−1
最终,lll 会停在最小满足条件的 radixradixradix 处。
🔁 实现代码如下:
while(l <= r){
long long mid = (l + r) >> 1;
long long n2 = f(b, mid);
if(n2 == -1 || n2 > n1) r = mid - 1;
else if(n2 < n1) l = mid + 1;
else r = mid - 1; // 继续向左逼近最小解
}
✅ Step 5:判断是否找到解
根据循环退出条件 l > r,可知:
- 若存在解,则
l就是最小合法 radixradixradix ; - 若不存在解,则
f(b, l) != n1,输出Impossible
if(f(b, l) == n1) cout << l;
else cout << "Impossible";
✅ 完整代码(C++)
#include <bits/stdc++.h>
using namespace std;
string a, b;
int t, rd;
long long f(string x, int r){
long long s = 0;
for(int i = 0; i < x.size(); i++){
int t = (x[i] <= '9') ? (x[i] - '0') : (x[i] - 'a' + 10);
if(s > (LLONG_MAX - t) / r) return -1;
s = s * r + t;
}
return s;
}
int main(){
cin >> a >> b >> t >> rd;
if(t == 2) swap(a, b);
long long n1 = f(a, rd);
int k = 0;
for(int i = 0; i < b.size(); i++){
int t = (b[i] <= '9') ? (b[i] - '0') : (b[i] - 'a' + 10);
k = max(k, t);
}
long long l = k + 1, r = max(l, n1);
while(l <= r){
long long mid = (l + r) >> 1;
long long n2 = f(b, mid);
if(n2 == -1 || n2 > n1) r = mid - 1;
else if(n2 < n1) l = mid + 1;
else r = mid - 1;
}
if(f(b, l) == n1) cout << l;
else cout << "Impossible";
return 0;
}
🚧 常见错误提醒
| 错误类型 | 具体表现 |
|---|---|
未处理 tag == 2 | 没有 swap 导致已知 radixradixradix 错位 |
| 转换函数未防止溢出 | 进制乘法可能导致 long long 溢出 |
| radixradixradix 范围确定错误 | 忽略了 k+1 的限制,或者最大值不合理 |
✅ 总结归纳
📌 核心方法总结
- 字符串进制转换函数的设计;
- radixradixradix 的合法范围确定:[k+1,n1][k+1, n1][k+1,n1];
- 二分查找最小合法 radixradixradix 满足条件。
📋 技术要点回顾
- 字符转十进制技巧:
'0'~'9'对应 000 ~ 999,'a'~'z'对应 101010 ~ 353535;; - 二分查找策略(最小满足条件);
- LLONG_MAX 防止溢出技巧。
📊 复杂度分析
- 时间复杂度:O(LlogN)\mathcal{O}(L \log N)O(LlogN),LLL 为字符串长度,NNN 为最大可能 radixradixradix
- 空间复杂度:O(1)\mathcal{O}(1)O(1)
🧠 思维拓展
- 如果输出最大合法 radixradixradix 解,该如何修改?
- 若 radixradixradix 不限定在 36 内(允许更大进制),如何支持?
更多推荐


所有评论(0)