PAT Advanced 1010. Radix

Posted tacticKing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PAT Advanced 1010. Radix相关的知识,希望对你有一定的参考价值。

PAT Advanced 1010

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

PAT 1010. Radix

1010. Radix (25)

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

分析

这道题的测试数据比较多,测试点10和7比较难过。首先如果不用二分搜索法,用暴力遍历的话,测试点7超时。如果用二分搜索法却没有考虑到long long int也会溢出的情况,则测试点10错误。

代码如下

#include<iostream>
#include<math.h>
#include<cctype>
#include<algorithm>
using namespace std;
/*将radix进制的数转化为十进制的数*/ 
long long int stonum(string s,long long int radix){ 
    long long int num=0,temp=0;
    for(int i=s.size()-1;i>=0;i--){
        if(isalpha(s[i]))
        num+=(s[i]-87)*pow(radix,temp++);
        else
        num+=(s[i]-'0')*pow(radix,temp++);
    }
    return num;
}
/*二分搜索法*/ 
long long int binsearch(long long int l,long long int r,long long int n1,string s){
    while(l<=r){
        long long int mid=(l+r)/2,sum=0,temp=0;
        sum=stonum(s,mid);
        if(sum==n1)
        return mid;
        if(sum<0) r=mid-1; // 这一步则是处理溢出情况,当溢出时,sum<0 
        else 
        sum<n1?l=mid+1:r=mid-1;
    }
    return -1;
}
int main(){
    string s1,s2,s;
    int tag,radix;
    long long int n1=0,n2=0; // 这题的数据可能很大都用long long int 
    cin>>s1>>s2>>tag>>radix;
    s=tag>1?s2:s1; //把s赋上tag所指的数 
    n1=stonum(s,radix); // 将已知radix的数转化为10进制 
    s=tag>1?s1:s2; // 把s赋上另一个数 
    
    /*下面开始去找所求数中所有位数最大的那位,因为所求radix至少比它大*/ 
    long long int max=-1;
    for(int i=s.size()-1;i>=0;i--){
        if(isalpha(s[i])&&s[i]-87>max)
        max=s[i]-87;
        else if(!isalpha(s[i])&&s[i]-'0'>max)
        max=s[i]-'0';
    }
    
    long long int l=max+1>2?max+1:2; // radix至少是2,确定radix下界l 
    /* radix则取十进制下的第一个数和下界中最大的加一 */ 
    long long int r=n1>l?n1+1:l+1; 
    if(binsearch(l,r,n1,s)==-1) cout<<"Impossible";
    else cout<<binsearch(l,r,n1,s); 
    return 0;
}

以上是关于PAT Advanced 1010. Radix的主要内容,如果未能解决你的问题,请参考以下文章

PAT Advanced Level 1010

PAT甲级 1010 Radix

PAT-1010 Radix

PAT 1010. Radix

PAT甲组 1010 Radix (二分)

PAT甲级 1010 Radix