PAT甲级1010

PAT1010 进制匹配问题

题目简介:

给出两个数字N1和N2,并给出其中一个数字的进制,找到一个合适的进制,使得另一个数字用该进制的情况下,使的两个数字的值相等。如果找不到这个进制,则输出Impossible。
注:为了方便描述,我们始终让N1存储已知进制的数字,让N2存储未知进制的数字,即如果tag==2,仅需要交换N1和N2即可。

题目分析:

该问题有几个关键点需要处理。首先是N1和N2都要转换成10进制进行比较;其次是N1和N2转换进制的时候,需要处理溢出的情况;再次,需要确定N2进制的上限和下限。

解决思路:

N1转换成10进制利用进制转化算法即可,按照题意来看,N1的10进制应该可以用long long表示最多10位的情况(反正这么做是可以通过的,如果真的无法表示的话,那么题目就过于复杂了!)。

对于N2进制区间确定的问题,可以这么理解:
N2进制的下限肯定是N2中出现的最大的数字(包括字母表示的)加1,在这里假设为low
N2进制的上限可以理解为N1+1和low中较大的那个,在这里假设为high,可以这么想,如果进制的上限high>lowhigh>N1,那么只要N2有一个非0位,则N2>N1是必然的,这就没有比较意义了。这也是本题思路上的一个卡点。同理,low>N1的情况,为了保证数字是有意义的,只能选择low为上限了(此时上下限相等),不过这种情况除非都是0,否则肯定是Impossible的。。

具体算法:

1. 快速转换映射 Map[]

为了后期进制转化时不用在判断数字还是字母,在这里直接使用一个Map数组映射,具体直接参考init函数的操作,很好理解。

2. k进制转化10进制算法 num2decimal()

核心思想就是一行代码,已经在代码中标出来了。不过本题要添加一句是否溢出或者超过进制上界的判断(只是针对N2的),如果这种情况发生了,说明N2在当前某进制下的数值肯定大于N1了,再继续向下判断没有任何意义了,相当于一个剪枝操作。

3. 二分查找算法 BSearch()

由于进制的范围未知,因此采取二分查找,来保证渐进复杂度满足要求。因为所有可能的区间范围是 [2,2631] [ 2 , 2 63 − 1 ] ,即long long的范围。 由于进制是有序的,所以使用二分查找,顺序查找会超时!自己写二分的时候,注意区间的控制,个人习惯是前开后闭。

cmp比较函数

用来比较数据大小的。因为考虑到N2溢出的情况,所以为了简化BSearch的代码思路,专门写了一个这个函数。

代码:

#include <bits/stdc++.h>
using namespace std;

using LL = long long;
const LL INF = (1LL << 63) - 1;
LL Map[130];   // 快速映射使用的,提供了一种解决问题的思路

void init() {
    for(char c = '0'; c <= '9'; ++c) {
        Map[int(c)] = c - '0';
    }
    for(char c = 'a'; c <= 'z'; ++c) {
        Map[int(c)] = c - 'a' + 10;
    }
}

// 把r进制的num转换成10进制,t是上界
LL num2decimal(const string& num, LL r, LL t) {
    LL sum = 0;
    int l = num.length();
    for(int i = 0; i < l; ++i) {
        if(sum < 0 || sum > t) { // 溢出或者超过上界,返回-1
            return -1;
        }
        sum = sum * r + Map[num[i]];  // 转换函数的核心思想
    }
    return sum;
}

LL max_digit(const string& num) {
    LL res = -1;
    for(int i = 0; i < num.length(); ++i) {
        if(Map[int(num[i])] > res) {
            res = Map[num[i]];
        }
    }
    return res;
}

int cmp(const string& N2, LL radix, LL t) {
    LL num = num2decimal(N2, radix, t);
    if(num < 0) {     // 溢出肯定是num大
        return 1;
    }
    if(t > num) {     // t比较大,确定t是上界
        return -1;
    } else if(t == num) { // 相等的情况
        return 0;
    } else {
        return 1;     // num比较大
    }
}
// 由于进制的范围未知,因此采取二分查找,来保证渐进复杂度满足要求
LL BSearch(const string& N2, LL low, LL high, LL t) {
    LL mid;
    while(low < high) {
        mid = (low + high) / 2;
        int flag = cmp(N2, mid, t);
        if(flag == 0) {   // 正好是相等的情况
            return mid;
        } else if(flag == -1) { // mid进制偏小
            low = mid + 1;
        } else {                // mid进制偏大
            high = mid;
        }
    }
    return -1;                  // 找不到
}

int main() {
    init();
    string N1, N2;
    LL tag, radix;
    cin >> N1 >> N2 >> tag >> radix;
    if(tag == 2) {  // 始终让N1是已知的
        N1.swap(N2);
    }
    LL t = num2decimal(N1, radix, INF);
    LL low = max_digit(N2);
    LL high = max(low, t) + 1;
    LL ans = BSearch(N2, low, high + 1, t);
    if(ans == -1) {
        cout << "Impossible" << endl;
    } else {
        cout << ans << endl;
    }
    return 0;
}

总结:

学习了进制转换和二分搜索,个人觉得比较好的一道题目。不过感觉题目有个迷惑人的地方:* If the solution is not unique, output the smallest possible radix. * 但是这里并没判断就能通过了。可能是只会有一个正确的进制解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值