PAT Advanced 1010. Radix
PAT Advanced 1010. Radix
1. Problem Description:
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 \(N_1\) and \(N_2\), your task is to find the radix of one number while that of the other is given.
2. 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.
3. 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.
4. Sample Input:
6 110 1 10
1 ab 1 2
5. Sample Output:
2
Impossible
6. Performance Limit:
Code Size Limit
16 KB
Time Limit
400 ms
Memory Limit
64 MB
思路:
这道题坑点挺多的。。。主要考察进制的转换,一开始的想法(注释掉的代码部分)是对已知基数的数字先求出其对应的十进制数字(这里我默认N1
的基数已知,如果tag
为2的话就交换N1
和N2
),然后因为题目要求输出最小的可能的解,所以从小到大遍历每一种基数判断是否存在N1==N2
,这里因为N2
随着基数递增,所以当N2>N1
时就结束遍历并输出Impossible
,基数的初值就选为N2
中最大数字+1。
第一次提交时testpoint7报time limit exceeded,testpoint10报wrong answer,检查代码感觉逻辑没问题,无奈只能参考大佬题解:1010. Radix (25)-PAT甲级真题(二分法)_the last number "radix" is the radix of n1 if "tag_柳婼的博客-CSDN博客 ,发现是数据类型的问题,这里基数和转换后的十进制数字可能很大,需要定义为long long类型(我一开始是定义为int类型),所以导致了testpoint10的wrong answer以及testpoint7遍历搜索的time limit exceeded。以后检查代码逻辑无问题后应该首先考虑数据类型问题QAQ,这里修改数据类型后通过testpoint10,参照大佬题解使用二分法后通过testpoint7。
这里二分法的使用其实还涉及三个细节问题,这也是这道题坑点多的原因:
- 二分法右端点的选择问题:左端点可以直接按照
N2
中最大数字+1选择,而右端点如何确定呢?大佬的题解是设置为max(num, low)
,其中num
为N1
对应的十进制数值,low
为左端点。这样做可以保证右端点不小于左端点,另外右端点设为num
的合理性可以对N2
分情况讨论得出,如果N2
有两位数以上,那么其最小为10
,此时右端点设为N1
对应的十进制数值已经将可能解包含在内。如果N2
只有1位,此时只有N2
等于N1
对应的十进制数值时才有解,其余情况无解,右端点设为max(num, low)
也无可厚非(这里还是解释不太清楚)。 - 二分法迭代选择左半区间或右半区间问题:这里我一开始是简单的根据
N2
对应的十进制数值选择左半或右半区间,但这样做是有问题的。原因在于基数过大时,N2
对应的十进制数值可能会溢出而变为负值(long long类型),这种情况应该选择左半区间,而如果不额外进行判断的话会因为N2<N1
而选择右半区间造成bug,这个问题是参考大佬题解才知道的,让我自己想可能几天也发现不了。。。 - 二分搜索得到的是最小可能基数问题:关于为什么二分搜索能保证我们得到的解是最小可能解,第一次看大佬题解的时候无法理解,这也是为什么我解题时选择从小到大遍历搜索的原因。但后面仔细想想,如果
N2
有两位数以上,那么它是一定随着基数递增而严格递增的,所以不存在多个解的情况。只有当N2
为一位数时,此时存在多解的情况(你可以说它属于大于它个位数字本身的任意进制),而大佬的题解是可以保证此时输出最小可能基数的。
My Code & Result:
// #include <iostream>
// #include <string>
// using namespace std;
// // first submit testpoint 7 time limit exceeded, testpoint 10 wrong answer
// int main(void)
// {
// string n1, n2;
// int tag=0, radix=0;
// //int num1=0, num2=0;
// unsigned int i=0; // iterator
// //int res=0; // result of radix
// long long num1=0, num2=0;
// long long res=0;
// cin >> n1 >> n2 >> tag >> radix;
// if(tag == 2) // swap n1 n2 if tag is 2
// {
// n1.swap(n2);
// }
// //cout << n1 << " " << n2 << " " << tag << " " << radix << endl;
// // if(tag == 1) // tag is 1, n1 already know
// // {
// num1=0;
// for(i=0; i<n1.length(); ++i) // calculate n1 in decimal
// {
// num1 *= radix;
// if(n1[i] >= '0' && n1[i] <= '9') // n1[i] is digit
// {
// num1 += (n1[i] - '0');
// }
// else if(n1[i] >= 'a' && n1[i] <= 'z') // n1[i] is alpha
// {
// num1 += (n1[i] - 'a' + 10);
// }
// }
// // cout << num1 << endl;
// res=0; // calculate initial radix
// for(i=0; i<n2.length(); ++i)
// {
// if(n2[i] > res) res = n2[i];
// }
// if(res>='0' && res<='9') res -= '0';
// else if(res>='a' && res<='z') res = res - 'a' + 10;
// //else if(res>='a' && res<='z') res -= 'a' + 10; // this way is wrong
// ++res;
// // cout << res << endl;
// // res = 2; // traverse n2 radix from 2
// for( ; ; )
// {
// num2=0;
// for(i=0; i<n2.length(); ++i) // calculate n2 in res radix
// {
// num2 *= res;
// if(n2[i] >= '0' && n2[i] <= '9') // n1[i] is digit
// {
// num2 += (n2[i] - '0');
// }
// else if(n2[i] >= 'a' && n2[i] <= 'z') // n1[i] is alpha
// {
// num2 += (n2[i] - 'a' + 10);
// }
// if(num2 > num1) // no match radix
// {
// printf("Impossible\n");
// return 0;
// }
// }
// if(n2.length() == 1 && num2 < num1) // avoid infinite loop when n2 only have 1 digit
// {
// printf("Impossible\n");
// return 0;
// }
// if(num2 == num1) // got match radix
// {
// printf("%lld\n", res);
// return 0;
// }
// ++res;
// }
// // }
// // else // tag is 2, n2 already know
// // {
// // }
// return 0;
// }
#include <iostream>
#include <string>
#include <algorithm> // max header
using namespace std;
long long getNum(string n2, long long radix);
long long find_radix(long long num1, string n2);
// first submit testpoint 7 time limit exceeded, testpoint 10 wrong answer
int main(void)
{
string n1, n2;
int tag=0, radix=0;
//int num1=0, num2=0;
unsigned int i=0; // iterator
//int res=0; // result of radix
long long num1=0; //, num2=0;
long long res=0;
cin >> n1 >> n2 >> tag >> radix;
if(tag == 2) // swap n1 n2 if tag is 2
{
n1.swap(n2);
}
//cout << n1 << " " << n2 << " " << tag << " " << radix << endl;
num1=0;
for(i=0; i<n1.length(); ++i) // calculate n1 in decimal
{
num1 *= radix;
if(n1[i] >= '0' && n1[i] <= '9') // n1[i] is digit
{
num1 += (n1[i] - '0');
}
else if(n1[i] >= 'a' && n1[i] <= 'z') // n1[i] is alpha
{
num1 += (n1[i] - 'a' + 10);
}
}
// cout << num1 << endl;
res = find_radix(num1, n2);
if(res != -1)
{
printf("%lld\n", res);
}
else
{
printf("Impossible\n");
}
return 0;
}
long long getNum(string n2, long long radix) // calculate n2 in res radix
{
long long num2=0;
for(unsigned int i=0; i<n2.length(); ++i) // calculate n2 in res radix
{
num2 *= radix;
if(n2[i] >= '0' && n2[i] <= '9') // n1[i] is digit
{
num2 += (n2[i] - '0');
}
else if(n2[i] >= 'a' && n2[i] <= 'z') // n1[i] is alpha
{
num2 += (n2[i] - 'a' + 10);
}
}
return num2;
}
long long find_radix(long long num1, string n2) // return -1 means Impossible, else return the radix
{
//long long radix=0;
long long left=0, right=0, mid=0;
long long num2=0;
for(unsigned int i=0; i<n2.length(); ++i) // calculate initial radix
{
if(n2[i] > left) left = n2[i];
}
if(left>='0' && left<='9') left -= '0';
else if(left>='a' && left<='z') left = left - 'a' + 10;
++left;
right = max(left, num1);
while(left <= right) // binary search
{
mid = (left + right) / 2;
num2 = getNum(n2, mid);
if(num2<0 || num1<num2) // select left half, the num2<0 is important!!!
{
right = mid - 1;
}
else if(num1>num2) // select right half
{
left = mid + 1;
}
else
{
return mid;
}
// if(num2 < num1) // select right half
// {
// left = mid+1;
// }
// else if(num2 > num1) // select left half
// {
// right = mid-1;
// }
// else // num1 == num2
// {
// return mid;
// }
}
return -1;
}
Compiler
C++ (g++)
Memory
592 / 65536 KB
Time
4 / 400 ms