PAT (Advanced Level) Practice - 1010 Radix(25 分)

题目链接:点击打开链接

题目大意:给定两个数,其中单个位置上的数值范围可以为 [0-z]。指定其中一个数的进制,试确定是否存在可能的进制让两数的实际值相等。(如果有多个答案,要求最小进制值)

解题思路:
1、input 中两个数字可以是 10 位数,虽然没有告诉 radix 的范围,但在9*10^10 10 1 200这个示例中,可以看到结果的 radix 也可以是很大的。从这个角度看,代码中将 radix 和两个数值都设定为 longlong 是合适的选择。
2、在计算另一个数的 radix 时,简单的遍历 [2, 1018]会超时。单调的区间很自然想到使用二分查找。
3、二分查找的上下界确定能减少耗时:下界选数字的所有位上的最大值+1;上界容易想当然的认为就是题中给定了 radix 的数的值。实际上,示例11 b 1 10就是一个反例,原因在于这个假设忽略了一位数的可能性,解决方案是在取给定 radix 的数值和下界中较大的那个数。
4、二分查找时,不可直接计算出某个 radix 下数的值,因为可能会 longlong 溢出。于是需要用特定的 cmp 函数,在累加的过程中判定是否大于另一个数。算是一种剪枝。
5、当两个数都是 1 时,输出 2。

AC 代码

#include<bits/stdc++.h>
#include<cmath>

#define mem(a,b) memset(a,b,sizeof a);
#define INF 0x3f3f3f3f

using namespace std;

typedef long long ll;

string a[3];
ll tag,radix,num;

int cal(char c)
{
    if(c>='0' && c<='9') return c-'0';
    else return c-'a'+10;
}

ll Any2D(string s, ll rdx)
{
    ll ans=0, tmp=1;
    for(int i=s.length()-1;i>=0;i--)
    {
        ans+=cal(s[i])*tmp;
        tmp*=rdx;
    }

    return ans;
}

int cmp(string s,ll rdx)
{
    ll ans=0, tmp=1;
    for(int i=s.length()-1;i>=0;i--)
    {
        ans+=cal(s[i])*tmp;
        if(ans>num) return 1;
        tmp*=rdx;
    }

    return ans==num?0:-1;
}

int maxR(string s)
{
    int ans=-1;
    for(int i=0;i<s.length();i++)
        ans=max(ans,cal(s[i]));

    return ans+1;
}

ll bs(int cur) // 此二分类型是:求最小的i,使得a[i] = key
{
    ll l=maxR(a[cur]), r=max(l,num), m, rs;
    while(l<r)
    {
        m=l+((r-l)>>1);
        rs=cmp(a[cur],m);
        if(rs==-1) l=m+1;
        else r=m;
    }

    return cmp(a[cur],r)==0?r:0;
}

int main()
{
    while(cin>>a[1]>>a[2]>>tag>>radix)
    {
        if(a[1]=="1" && a[2]=="1") puts("2");
//        else if(a[1]==a[2]) printf("%lld\n",radix);
        else
        {
            num=Any2D(a[tag],radix);
            int cur=tag==1?2:1, ans=bs(cur);

            if(ans) printf("%lld\n",ans);
            else puts("Impossible");
        }
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/dream_weave/article/details/81273891