PAT 1010 Radix(大进制转换+二分查找+剪枝)

原题地址

https://www.patest.cn/contests/pat-a-practise/1010

给定一个数字a及其进制,判断另一个数b是不是a在其他进制下的表示。(a,b较大,以字符串形式给出)

解题思路

本题考察进制转换,但是和之前做过的一次性转换题都不太一样,即要求搜索出满足b.value = a.value时b的进制数。

判断两进制数是否相等的基本思路:两个数都用10进制表示,判断是否相等即可。

对于本题,先计算出a的十进制数,记为known(由于a不超过10位,因此用long long int基本可以转换出来)

刚开始用了穷举的办法,要查找的进制数base从2开始递增,直到b在某个base下的十进制数等于known,然而第7组数据始终超时无法通过。这时候想到必须要提高查找效率,要剪枝。
很直观的想到,可以用这个字符串里的最大数字maxNum来确定最小基数,不必每次都从2开始,再次尝试,依然超时啊=.=

最后搜到大神的博客,才知道还可以用二分查找优化!(真是惭愧,本应该想到继续优化查找方式的)另外,还要建立新的compare方法来对判读剪枝,不必计算出base进制下b完整的值再与known比较,当累加值已经大于known时就可以进行下一次二分。

值得注意的点:

  • 数据范围:由于没有说明radix的范围,因此最好也声明为long long,涉及转换时的中间变量也要声明为long long
  • 二分时,下界为该字符串的最小基数(即最大数字+1),上界为max(最小基数,known)。用known而不是用radix是因为可能radix很小,a却很大。
  • 两个数字长的完全一样时可以直接输出radix。

AC代码

通过所有测试的代码:

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
#define toNum(c) ((c >= '0' && c <= '9')? (c-'0'):(c-'a'+10))

const int maxLen = 15;
LL tag, radix, known; //必须都声明为LL

LL toDecimal(const string &str, LL radix) //radix制的str转long long int
{
    LL ans = 0;
    int len = str.size();
    for (int i = 0; i < len; ++i)
        ans = ans*radix + toNum(str[i]);
    return ans;
}

int maxNum(const string &str) //找str中的最大字符对应的数字,即最小基数
{
    int len = str.size(), d = -1;
    for (int i = 0; i < len; ++i)
        d = (toNum(str[i]) > d) ? toNum(str[i]) : d;
    return d+1; //别忘记+1
}

int compare(const string & s, LL base)//比较base进制的s和已知数的大小
{
    LL ans = 0, d = 1;
    int num;
    for (int i = s.size()-1; i >= 0; --i)
    {
        num = toNum(s[i]);
        ans += num*d;
        if (ans > known)
            return 1; //不必算出全部值,s已经大于known
        d *= base;
    }
    if (ans == known) return 0; //在base进制下两者相等
    else return -1; //在base进制下s小于known
}

LL bin_search(const string &s) //二分判断s和known是否可能在某个进制相等
{
    LL low = maxNum(s), high = max(low, known), mid; //注意high的取值
    while (low <= high)
    {
        mid = (low+high) / 2;
        int cmp_res = compare(s, mid);
        if (cmp_res == 0) //相等
            return mid;
        if (cmp_res == 1) //大于
            high = mid-1;
        else //小于
            low = mid+1;
    }
    return -1;
}

int main()
{
    string s[3];
    cin >> s[1] >> s[2] >> tag >> radix;
    if (s[1] == s[2]) //如果两者相等直接返回radix
    {
        cout << radix << endl;
        return 0;
    }
    known = toDecimal(s[tag], radix); //把已知的转换为十进制数
    LL ans;
    if (tag == 1)
        ans = bin_search(s[2]);
    else ans = bin_search(s[1]);
    if(ans == -1)
        cout << "Impossible" << endl;
    else
        cout << ans << endl;
    return 0;
}

只有第7组超时的代码(是错误的!必须加快查找):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
const int maxLen = 15;
#define toNum(c) ((c >= '0' && c <= '9')? (c-'0'):(c-'a'+10))

LL toDecimal(const string &str, int radix)
{
    LL ans = 0;
    int len = str.length();
    for (int i = 0; i < len; ++i)
        ans = ans*radix + toNum(str[i]);
    return ans;
}

int find_MaxNum(const string &str)
{
    int len = str.length(), maxNum = -1;
    for (int i = 0; i < len; ++i)
        maxNum = (toNum(str[i]) > maxNum) ? toNum(str[i]) : maxNum;
    return maxNum+1;
}

int judge(const string & s, LL cmp)
{
    LL cur;
    int base = find_MaxNum(s);;
    while(1)
    {
        cur = toDecimal(s, base);
        if (cur == cmp)
            return base;
        else if (cur > cmp)
            return -1;
        base++;
    }
}

int main()
{
    string s[3];
    int tag, radix, isSame = -1;
    LL cmp;
    cin >> s[1] >> s[2] >> tag >> radix;
    cmp = toDecimal(s[tag], radix);
    if (s[1] == s[2])
    {
        cout << radix << endl;
        return 0;
    }
    if (tag == 1)
        isSame = judge(s[2], cmp);
    else
        isSame = judge(s[1], cmp);
    if(isSame == -1)
        cout << "Impossible" << endl;
    else
        cout << isSame << endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值