原题地址
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;
}