1010 Radix (25分)
题目链接: PAT A 1010
这道题难度比较大,而且测试点还非常多,我debug就花了一天时间,尽量为大家讲解的清楚一些吧。
思路分析:这题给两个数和其中一个数的进制,要找出另外一个数是什么进制的时候使得这两个数相等。注意题目里数位的范围是0-35,但是这个进制不一定小于36,而是从2到int最大值甚至是long long最大值的任意一个数,比如1可以是二进制数也可以是十进制数。为了方便起见,如果tag等于2,那么我们就交换n1和n2,使得已知进制的数是n1,未知进制的数是n2。然后就可以将两个数都转换成十进制去比较他们的大小,注意不可以暴力枚举所有的进制,因为这样数据量过大,会超时,这就需要用到二分法找n2的进制:二分法的下界是n2的所有数位中最大值加一(八进制是由0-7构成的,十进制是由0-9构成的,一个数最小的进制就是数位最大的值加一),上界就是n1转换为十进制之后的值(有可能小于下界)和下界中的大者(因为如果在大,那n2转换成十进制就一定大于那个已知进制数的十进制了。当n2只有一位时,其大小与基数无关,例如:只要基数大于8,那么8这个数不管是在16进制下还是20进制下,都是8(10进制下),因为任何基数的0次方都是1嘛;当N2有两位或以上时,倒数第二位最小是1,那么基数为n1时,这个数就必然会大于等于n1,所以再大就没有必要比较了)。经过测试 :本题数据默认保证n1(已知进制的数)转换成十进制的数时不超过long long(题目中没有说),因此只要对n2在转换成十进制时判断是否溢出(只要在转换过程中某步小于0就是溢出,比如int范围是2的31次幂-1,那么如果int a = 2的31次幂,那么输出这个a就会显示一个负数)。由于n1不超过long long,所以如果n2溢出,那么就是超过了long long的范围,就肯定大于n1了。
1.首先定义一个INF代表long long的最大范围,值为2的63次幂-1
2.写一个转换成十进制的函数,当字符串是n1的时候if(ans < 0 || ans > t)是不会执行的,n2时候才有可能执行,此时ans小于0代表溢出,ans大于t代表超过n1了,都返回-1
3.写一个比较函数,作用是比较n1转换成十进制和n2转换成十进制数的大小
4.二分查找函数,不断改变n2进制去比较n1转换成十进制和n2转换成十进制数的大小,如果相等了就说明此时的进制就是我们要找的进制。如果没有找到就返回-1代表没有找到
5.找最大数位的函数,输入的是字符串n2,返回最大数位+1即二分查找的下界
6.难点基本已经解释清楚,其余部分见注释
AC代码:
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
long long INF = pow(2, 63) - 1; //long long范围内最大的数
long long change(string s, long long radix, long long t) { //将字符串转换为十进制
long long ans = 0; //计算s代表的十进制数
for(int i = 0; i < s.length(); i++) {
if(s[i] >= '0' && s[i] <= '9') //数位是0-9
ans = ans * radix + s[i] - '0';
else //数位是a-z,代表10-35
ans = ans * radix + s[i] - 'a' + 10;
if(ans < 0 || ans > t) //ans小于0代表溢出,ans大于t代表超过了n1表示的十进制数
return -1;
}
return ans;
}
int compare(string s, long long radix, long long t) { //n2的十进制与t比较
long long num = change(s, radix, t); //计算n2代表的十进制
if(num < 0) //这说明n2代表的十进制一定大于n1代表的十进制
return 1;
if(num > t)
return 1;
else if(num == t)
return 0;
else
return -1;
}
long long binarysearch(string s, long long left, long long right, long long t) { //二分查找
long long mid;
while(left <= right) {
mid = (left + right) / 2;
int flag = compare(s, mid, t);
if(flag == 0) //n1代表的十进制与n2代表的十进制相等
return mid;
else if(flag < 0) //n1的大,要增大n2的进制范围
left = mid + 1;
else //n1的小,减小n2进制范围
right = mid - 1;
}
return -1; //没找到使他们相等的进制
}
int findmax(string s) { //找最大数位,用于计算下界
int ans = 0;
for(int i = 0; i < s.length(); i++) {
if(s[i] >= '0' && s[i] <= '9') {
if(s[i] - '0' > ans)
ans = s[i] - '0';
}
else {
if(s[i] - 'a' + 10 > ans)
ans = s[i] - 'a' + 10;
}
}
return ans + 1; //返回最大数位加1代表下界
}
int main() {
string a, b, temp; //a代表n1,b代表n2
int tag, radix;
cin >> a >> b >> tag >> radix;
if(tag == 2) { //如果给定的是n2的radix,方便起见,交换n1,n2,使得已知的永远是n1的进制
temp = a;
a = b;
b = temp;
}
long long t = change(a, radix, INF); //计算n1代表的十进制数
long long low = findmax(b); //下界
long long high = max(low, t); //上界
long long ans = binarysearch(b, low, high, t); //这里的t就是我们刚计算出的t,即n1代表的十进制数
if(ans == -1) //没找到
cout << "Impossible";
else
cout << ans;
return 0;
}