PAT Advanced 1010. Radix

发布时间 2023-05-20 14:47:27作者: 十豆加日月

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的话就交换N1N2),然后因为题目要求输出最小的可能的解,所以从小到大遍历每一种基数判断是否存在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。

这里二分法的使用其实还涉及三个细节问题,这也是这道题坑点多的原因:

  1. 二分法右端点的选择问题:左端点可以直接按照N2中最大数字+1选择,而右端点如何确定呢?大佬的题解是设置为max(num, low),其中numN1对应的十进制数值,low为左端点。这样做可以保证右端点不小于左端点,另外右端点设为num的合理性可以对N2分情况讨论得出,如果N2有两位数以上,那么其最小为10,此时右端点设为N1对应的十进制数值已经将可能解包含在内。如果N2只有1位,此时只有N2等于N1对应的十进制数值时才有解,其余情况无解,右端点设为max(num, low)也无可厚非(这里还是解释不太清楚)。
  2. 二分法迭代选择左半区间或右半区间问题:这里我一开始是简单的根据N2对应的十进制数值选择左半或右半区间,但这样做是有问题的。原因在于基数过大时,N2对应的十进制数值可能会溢出而变为负值(long long类型),这种情况应该选择左半区间,而如果不额外进行判断的话会因为N2<N1而选择右半区间造成bug,这个问题是参考大佬题解才知道的,让我自己想可能几天也发现不了。。。
  3. 二分搜索得到的是最小可能基数问题:关于为什么二分搜索能保证我们得到的解是最小可能解,第一次看大佬题解的时候无法理解,这也是为什么我解题时选择从小到大遍历搜索的原因。但后面仔细想想,如果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