一、问题描述
输入两个数N1、N2,和一个标记tag、一个基数radix,其意义是:tag为1时,radix是N1的基数;tag为2时,radix是N2的基数。方便起见,以下假定tag为1,则目标就是找到一个尽可能小的基数,使N2在此基数下的值等于N1在radix下的值。例如二进制下的110等于十进制下的6。
二、思路
做过的都知道,思路是不难的,就是坑实在不少,要想的全面一点。
1、暴力算法:基数从2一直取到N1,当基数能使N2=N1时停止:有测试用例运行超时,扑街。
2、二分法:
基数的最小值:N2的最大权值+1(数=权值+基数),例如N2中最大的权值是a(也就是10),那么这个数至少是11进制的。
基数的最大值:
N1+1。为什么是N1+1呢?当N2只有一位时,其大小与基数无关,例如:只要基数大于8,那么8这个数不管是在16进制下还是20进制下,都是8(10进制下),因为任何基数的0次方都是1嘛;当N2有两位或以上时,倒数第二位最小是1,那么基数为N1+1时,这个数就必然会大于等于N1+1,所以再大就没有必要比较了。
那么能不能是N1呢?反正在我这个程序里是不行的,因为代码里当最小值(start)大于最大值(end)时,会返回0(Impossible),那么当N1=10,N2=a时,start=a+1=11,end=10,所以输出Impossible,但显然在11进制下10=a,出现这种情况的原因就是:大于等于a的数虽然是以一位呈现出来,但是真实值在10进制下是两位。(为了统一,可以把10进制下的两位数10,看作是11进制下的一个符号‘10’,哈哈哈哈哈)
常见的坑:
基数可能会很大,导致N2按此基数算出来之后超过longlong的表示范围,不过我们只需要加一条判断,当N2溢出变成负数时(显然使N1=N2成立的基数如果存在的话一定会小于当前基数),我们去小的那一半里去找基数;
基数最小为2;
基数不能小于等于N2里最大的权值;
等等……
三、代码
#include <iostream>
using namespace std;
long long int getNum(string str,long long int radix){
//作用:按照基数radix,将字符串转化str为数
//可能会很大,用longlong
long long int sum=0;
for (size_t i = 0; i < str.length(); ++i) {
if (str[i] <= '9')sum = sum * radix + (str[i] - '0');
//注意‘0’-'9’,和a-z不连续
else if(str[i]<='z')sum=sum*radix+(str[i]-'a'+10);
}
return sum;
}
long long int getRadix(long long int num1,string str,long long int start,long long int end){
//主要滴代码
if(start==end){
//等号单独讨论,否则可能死循环
if(getNum(str,start)==num1)return start;
else return 0;
}
else if(start<end){
long long int radix=(start+end)/2;
//二分查找
if(getNum(str,radix)==num1)return radix;
//num2>num1或num2<0都表示这个radix取得过大了,需要减小
else if(getNum(str,radix)>num1||getNum(str,radix)<0)return getRadix(num1,str,start,radix);
//num2<num1,说明radix取得比较小,所以要往大了取
else return getRadix(num1,str,radix+1,end);
}
return 0;
}
int getMinRadix(string num2){
//作用:找到num2的最小基数
//例如:num2中最大的权值为b时,最小基数就是b+1=12
char max='0';
for (int i = 0; i < num2.length(); ++i)
if(num2[i]>max)max=num2[i];
if (max <= '9')return max-'0'+1;
else return max-'a'+11;
}
int main()
{
string str1,str2;
int tag;
long long int radix;
cin>>str1>>str2>>tag>>radix;
long long int num1;
//根据tag=1或2,作相应调整##
if(tag==1){
num1=getNum(str1,radix);
}
else{
num1=getNum(str2,radix);
str2=str1;
}
//几种特殊情况
if(num1==0&&str2=="0"){//试了一下,测试用例没有这种情况
cout<<"2";
return 0;
}
auto res=getRadix(num1,str2,getMinRadix(str2),num1+1);
if(res==0){
cout<<"Impossible";return 0;
}
cout<<res;
return 0;
}
明明刚吃完午饭,抬头一看,又到了吃晚饭的点了
w(゜Д゜)w
二刷:
#include<iostream>
using namespace std;
long long int getNum(string s, long long int radix) {
long long int sum = 0;
for (int i = 0; i < s.size(); i++) {
int val = s[i] >= 0 && s[i] <= '9' ? s[i] - '0' : s[i] - 'a' + 10;
sum = sum * radix + val;
}
return sum;
}
int main() {
string s1, s2;
int tag, maxR = 0;
long long int radix;
cin >> s1 >> s2 >> tag >> radix;
long long int a = tag == 1 ? getNum(s1, radix) : getNum(s2, radix);
s2 = tag == 1 ? s2 : s1;
for (int i = 0; i < s2.size(); i++) {
if (s2[i] <= '9' && s2[i] >= '0')maxR = maxR > s2[i] - '0' ? maxR : s2[i] - '0';
else if (s2[i] >= 'a' && s2[i] <= 'z')maxR = maxR > s2[i] - 'a' + 10 ? maxR : s2[i] - 'a' + 10;
}
long long int start = maxR + 1, end = a + 1;
while (start <= end) {
long long int ans = (start + end) / 2;
long long int temp = getNum(s2, ans);
if (start == end && temp != a) {
break;
}
else if (temp == a) {
cout << ans << endl;
return 0;
}
else if (temp > a || temp < 0)end = ans;
else
start = ans + 1;
}
cout << "Impossible" << endl;
return 0;
}