Given a pair of positive integers, for example, 6 and 110, can this equation 6 = 110 be true? The answer is yes
, if 6 is a decimal number and 110 is a binary number.
Now for any pair of positive integers N1 and N2, your task is to find the radix of one number while that of the other is given.
Input Specification:
Each input file contains one test case. Each case occupies a line which contains 4 positive integers:
N1 N2 tag radix
Here N1
and N2
each has no more than 10 digits. A digit is less than its radix and is chosen from the set { 0-9, a
-z
} where 0-9 represent the decimal numbers 0-9, and a
-z
represent the decimal numbers 10-35. The last number radix
is the radix of N1
if tag
is 1, or of N2
if tag
is 2.
Output Specification:
For each test case, print in one line the radix of the other number so that the equation N1
= N2
is true. If the equation is impossible, print Impossible
. If the solution is not unique, output the smallest possible radix.
Sample Input 1:
6 110 1 10
Sample Output 1:
2
Sample Input 2:
1 ab 1 2
Sample Output 2:
Impossible
Solve
这道题目要求找出一个合适的进制,使得两个数在不同进制下的值相等。具体来说:
- 给定两个数N₁和N₂,其中一个数的进制已知
- 需要找出另一个数的进制,使得它们的十进制值相等
- 如果有多个可能的进制,输出最小的一个
- 如果不存在这样的进制,输出”Impossible”
关键思路
- 进制转换:将已知进制的数转换为十进制,作为目标值
- 确定搜索范围:
- 下限:未知进制数中出现的最大数位值+1
- 上限:目标值(可以证明不需要搜索比目标值更大的进制)
- 二分搜索:在确定的范围内搜索合适的进制
- 处理边界情况:包括溢出、无解等情况
进制范围的确定
进制的下限很容易理解:必须大于数字中出现的最大数位值。例如,如果数字包含’b’(代表11),则进制至少为12。
进制的上限为什么是目标值?这可以通过数学推导得出:
- 设未知进制为r,长度为n的数字为a₁a₂…aₙ
- 其十进制值为:a₁×r^(n-1) + a₂×r^(n-2) + … + aₙ
- 当r足够大时,这个值主要由首项决定:a₁×r^(n-1)
- 要使这个值等于目标值t,必须有r^(n-1)≤t/a₁
- 由于n≥2且a₁≥1,可知r≤t
因此,我们只需要在[下限, 目标值]范围内搜索。
为什么使用二分搜索
由于随着进制的增加,数字的值单调递增,可以使用二分搜索提高效率。对于可能很大的目标值(最多10位数字),线性搜索效率太低。
这里使用优化后的整数二分模板。
为什么单独处理单字符情况
单字符情况下,解是直接可以计算的,不需要二分搜索:
- 对于单个字符c,在任何进制r(r > c的值)下,其值都是c本身
- 因此,如果有解,解一定是(数位值+1)
例如,字符’7’在任何r≥8的进制下值都是7,所以不需要搜索。
从数学角度看,单字符的情况有一个直观的解:
- 如果字符值等于目标值,则进制至少需要比字符值大1
- 所有大于等于(字符值+1)的进制都是可行的,我们取最小的
代码实现
#include<iostream>
#include<string>
#include<climits>
using namespace std;
// 获取字符对应的数值
int getDigitValue(char c) {
if(isdigit(c)) return c - '0';
if(islower(c)) return c - 'a' + 10;
return -1; // 错误情况
}
// 从左到右将指定进制的数转换为十进制,提前检测溢出
long long to10Dex(const string& num, long long radix) {
long long result = 0;
for(char c : num) {
int digit = getDigitValue(c);
// 检查溢出
if(result > (LLONG_MAX - digit) / radix)
return LLONG_MAX; // 表示溢出
result = result * radix + digit;
}
return result;
}
long long findSmallestRadix(string numA, long long targetValue) {
if(numA.length() == 1) { // 单个数值在任何进制的值都不会变化,故直接特殊处理即可, 无需再进行二分查找
int digit = getDigitValue(numA[0]);
return digit == targetValue ? digit + 1 : -1;
}
char it = *max_element(numA.begin(), numA.end());
long long low = (isdigit(it) ? it - '0': 10 + it - 'a'); // 进制最低是待比较数的最高位数+1
long long high = max(low + 1, targetValue + 1); // 进制最高是另一个已确定数的数值 实际上应该设定为 LONG_MAX
// 优化后的整数二分,要求 low,high 为要搜索范围的 [l-1, r+1]
while(low != high - 1) {
long long mid = low + (high - low) / 2;
long long t = to10Dex(numA, mid);
if(t > targetValue)
high = mid;
else
low = mid;
}
long long value = to10Dex(numA, low);
if(value == targetValue)
return low;
return -1;
}
int main() {
string num[2];
int tag;
long long radix;
cin >> num[0] >> num[1] >> tag >> radix;
long long result = findSmallestRadix(num[2 - tag], to10Dex(num[tag - 1], radix));
if(result != -1)
cout << result << endl;
else
cout << "Impossible" << endl;
return 0;
}
这种方法有效地解决了进制转换问题,并能处理各种边界情况。时间复杂度为O(log(target)×length),其中target是目标值,length是未知进制数的长度。