Given a pair of positive integers, for example, 6 and 110, can this equation 6 = 110 be true? The answer is yes, if 6 is a decimal number and 110 is a binary number.
Now for any pair of positive integers N 1 N_1 N1 and N 2 N_2 N2, your task is to find the radix of one number while that of the other is given.
Each input file contains one test case. Each case occupies a line which contains 4 positive integers:
N1 N2 tag radix
Here N1 and N2 each has no more than 10 digits. A digit is less than its radix and is chosen from the set { 0-9, a-z } where 0-9 represent the decimal numbers 0-9, and a-z represent the decimal numbers 10-35. The last number radix is the radix of N1 if tag is 1, or of N2 if tag is 2.
For each test case, print in one line the radix of the other number so that the equation N1 = N2 is true. If the equation is impossible, print Impossible. If the solution is not unique, output the smallest possible radix.
6 110 1 10
2
1 ab 1 2
Impossible
用两个字符串来接收 N 1 N1 N1 和 N 2 N2 N2 ,便于进行处理。用两个整形来接收标志位和进制位,将所给出进制的数转化成十进制,未给出进制位的数,使用二分法,找出最小的进制位。
基数下界这个问题其实很容易想到,比如说下面这个用例:
输入:1 1z1 1 10
输出:Impossible
z 不可能出现在10进制中。所以我在 judge 中加入了 3.2 中提到的部分来加以判断。
有了上面所述的思路,马上写出了第一份代码
#include
#include
using namespace std;
string n1,n2; //两个数用string读入
int tag,radix;
int size1,size2; //两个数的位数,便于使用pow函数计算
int num1,num2; //两个数的实际大小
int l,r,ans; //二分法求解
int todigit(char a) { //从char得出每一位数
if(a<='9'&&a>='0') {
return a-'0';
} else if(a<='z'&&a>='a') {
return a-'a'+10;
}
}
int judge(int radix) { //判断二分是否正确
int num=0;
if(tag==1) {// tag==1时候
for(int i=0; i<size2; i++) {
if(todigit(n2[i])>radix) {// 此处有问题,当有数大于radix时,肯定不对
return -1;
}
num+=(todigit(n2[i])*pow(radix,size2-i-1)); //计算数
}
if(num==num1) {
return 0;
} else if(num>num1) {
return 1;
} else {
return -1;
}
} else {
for(int i=0; i<size1; i++) {
if(todigit(n1[i])>radix) {
return -1;
}
num+=(todigit(n1[i])*pow(radix,size1-i-1));
}
if(num==num2) {
return 0;
} else if(num>num2) {
return 1;
} else {
return -1;
}
}
return 0;
}
int main() {
cin>>n1>>n2>>tag>>radix;
size1=n1.size();
size2=n2.size();
num1=0,num2=0;
// 计算已经给出基数的数的十进制值
if(tag==1) {
for(int i=0; i<size1; i++) {
num1+=(todigit(n1[i])*pow(radix,size1-i-1));
}
} else {
for(int i=0; i<size2; i++) {
num2+=(todigit(n2[i])*pow(radix,size2-i-1));
}
}
//二分法求解
l=2;
r=100;
ans=-1;
while(l<=r) {
int mid=(l+r)/2;
int k=judge(mid);
if(k==0) {
ans=mid;
r=mid-1;
} else if(k==-1) {
l=mid+1;
} else {
r=mid-1;
}
}
if(ans==-1) {
cout<<"Impossible";
} else {
cout<<ans;
}
return 0;
}
很荣幸地没有过(18/25)。

思考了一会以后,想到了一点,虽然在第一份代码中,我已经考虑了求到最小的进制,但是 0-8 的进制应该是 9 而不是 8
输入:8 8 1 10
正确输出:9
第一份代码输出:8
所以应该修改下面的地方为 >=
if(todigit(n2[i])>=radix) {
return -1;
}
和
if(todigit(n1[i])>=radix) {
return -1;
}
由于就修改了这个地方就去提交了。
#include
#include
using namespace std;
string n1,n2; //两个数用string读入
int tag,radix;
int size1,size2; //两个数的位数,便于使用pow函数计算
int num1,num2; //两个数的实际大小
int l,r,ans; //二分法求解
int todigit(char a) { //从char得出每一位数
if(a<='9'&&a>='0') {
return a-'0';
} else if(a<='z'&&a>='a') {
return a-'a'+10;
}
}
int judge(int radix) { //判断二分是否正确
int num=0;
if(tag==1) {// tag==1时候
for(int i=0; i<size2; i++) {
if(todigit(n2[i])>=radix) {// 此处有问题,当有数大于radix时,肯定不对
return -1;
}
num+=(todigit(n2[i])*pow(radix,size2-i-1)); //计算数
}
if(num==num1) {
return 0;
} else if(num>num1) {
return 1;
} else {
return -1;
}
} else {
for(int i=0; i<size1; i++) {
if(todigit(n1[i])>=radix) {
return -1;
}
num+=(todigit(n1[i])*pow(radix,size1-i-1));
}
if(num==num2) {
return 0;
} else if(num>num2) {
return 1;
} else {
return -1;
}
}
return 0;
}
int main() {
cin>>n1>>n2>>tag>>radix;
size1=n1.size();
size2=n2.size();
num1=0,num2=0;
// 计算已经给出基数的数的十进制值
if(tag==1) {
for(int i=0; i<size1; i++) {
num1+=(todigit(n1[i])*pow(radix,size1-i-1));
}
} else {
for(int i=0; i<size2; i++) {
num2+=(todigit(n2[i])*pow(radix,size2-i-1));
}
}
//二分法求解
l=2;
r=100;
ans=-1;
while(l<=r) {
int mid=(l+r)/2;
int k=judge(mid);
if(k==0) {
ans=mid;
r=mid-1;
} else if(k==-1) {
l=mid+1;
} else {
r=mid-1;
}
}
if(ans==-1) {
cout<<"Impossible";
} else {
cout<<ans;
}
return 0;
}

很高兴多对了一分。
既然这道题的基数可以很大,那么int类型势必是不太够的,本来考虑到既然 pow()函数返回值类型是 double,那我也用 double不就行了(毕竟 double 超级大啊!!!!),但是用 double 在暴力遍历法中用是可以的,但是在使用二分法的时候明显会出现小数部分,这就会给最后的判断带来麻烦。
这个时候只能退而求其次,用 long long ,说是退而求其次,但事实证明,long long 在这道题中是够用的,而且后来在查阅资料的时候发现,大家遇事不决都会用 long long 这个整型数扛把子
| 数据类型 | 取值范围 |
|---|---|
int | 2147483648~2147483647 |
long | 2147483648~2147483647 |
long long | -9223372036854775808 ~ 9223372036854775807 |
当然这里的数值溢出指的不是指已知基数的字符串的数值会溢出,而是指在二分搜索过程中未知基数的字符串的值会在数制转换之后溢出为负数。这样一来,就会导致原本应该是当前基数过大,应该使基数上界下移,但变为负数之后,判断条件以为基数过小了,就会调整使基数下界上移,这与我们二分搜索的初衷背道而驰。
注意啊,我下面这份代码里面,二分的上界 r 开了100,拿了(24/25)
#include
#include
using namespace std;
string n1,n2; //两个数用string读入
int tag;
long long radix;
int size1,size2; //两个数的位数,便于使用pow函数计算
long long num1,num2; //两个数的实际大小
long long l,r,ans; //二分法求解
int todigit(char a) { //从char得出每一位数
if(a<='9'&&a>='0') {
return a-'0';
} else if(a<='z'&&a>='a') {
return a-'a'+10;
}
}
int judge(long long radix) { //判断二分是否正确
long long num=0;
if(tag==1) {// tag==1时候
for(int i=0; i<size2; i++) {
if(todigit(n2[i])>=radix) {// 此处有问题,当有数大于radix时,肯定不对
return -1;
}
num+=(todigit(n2[i])*pow(radix,size2-i-1)); //计算数
}
if(num==num1) {
return 0;
} else if(num>num1||num<0) {
return 1;
} else {
return -1;
}
} else {
for(int i=0; i<size1; i++) {
if(todigit(n1[i])>=radix) {
return -1;
}
num+=(todigit(n1[i])*pow(radix,size1-i-1));
}
if(num==num2) {
return 0;
} else if(num>num2||num<0) {
return 1;
} else {
return -1;
}
}
return 0;
}
int main() {
cin>>n1>>n2>>tag>>radix;
size1=n1.size();
size2=n2.size();
num1=0,num2=0;
// 计算已经给出基数的数的十进制值
if(tag==1) {
for(int i=0; i<size1; i++) {
num1+=(todigit(n1[i])*pow(radix,size1-i-1));
}
} else {
for(int i=0; i<size2; i++) {
num2+=(todigit(n2[i])*pow(radix,size2-i-1));
}
}
//二分法求解
l=2;
r=100;
ans=-1;
while(l<=r) {
long long mid=(l+r)/2;
int k=judge(mid);
if(k==0) {
ans=mid;
r=mid-1;
} else if(k==-1) {
l=mid+1;
} else {
r=mid-1;
}
}
if(ans==-1) {
cout<<"Impossible";
} else {
cout<<ans;
}
return 0;
}

原本我以为进制的范围是 2 ~ 36,毕竟用来代替数字的字符也就指定了 36 个,所以我在二分的上界 r 开了100 ,当时感觉自己已经保守了。但是当很多测试用例跑不通时,我开始思考基数的上界是不是不止 36 ,比如下面这个用例:
输入:42949672 10 1 10
输出:42949672
二分的上界 r 开了10000000000 就AC了。
#include
#include
using namespace std;
string n1,n2; //两个数用string读入
int tag;
long long radix;
long long num1,num2; //两个数的实际大小
long long l,r,mid,ans; //二分法求解
long long todigit(char a) { //从char得出每一位数
if(a<='9'&&a>='0') {
return a-'0';
} else if(a<='z'&&a>='a') {
return a-'a'+10;
} else {
return -1;
}
}
long long strTo(string str,long long radix) {
int size=str.size();
long long num,cur;
num=0;
for(int i=0; i<size; i++) {
cur=todigit(str[i]);
if(cur>=0&&cur<radix) {
num+=(cur*pow(radix,size-i-1));
} else {
return -1;
}
}
return num;
}
int judge(long long radix) { //判断二分是否正确
long long num=0;
if(tag==1) {// tag==1时候
num=strTo(n2, radix);
if(num==num1) {
return 0;
} else if(num>num1||num<-1) {
return 1;
} else {
return -1;
}
} else {
num=strTo(n1, radix);
if(num==num2) {
return 0;
} else if(num>num2||num<-1) {
return 1;
} else {
return -1;
}
}
return 0;
}
int main() {
cin>>n1>>n2>>tag>>radix;
num1=0,num2=0;
// 计算已经给出基数的数的十进制值
if(tag==1) {
num1=strTo(n1,radix);
} else {
num2=strTo(n2,radix);
}
//二分法求解
l=2;
r=10000000000;
ans=-1;
while(l<=r) {
mid=(l+r)/2;
int k=judge(mid);
if(k==0) {
ans=mid;
r=mid-1;
} else if(k==1) {
r=mid-1;
} else {
l=mid+1;
}
}
if(ans==-1) {
cout<<"Impossible";
} else {
cout<<ans;
}
return 0;
}
考虑到第一份AC代码重复的部分太多,耦合度实在太高(虽然感觉ACM的代码不讲究美观),重新修改了下面这一份代码,和上面的第一份AC代码没有本质区别。
#include
#include
using namespace std;
string n1,n2; //两个数用string读入
int tag;
long long radix;
long long num1,num2; //两个数的实际大小
long long l,r,mid,ans; //二分法求解
long long todigit(char a) { //从char得出每一位数
if(a<='9'&&a>='0') {
return a-'0';
} else if(a<='z'&&a>='a') {
return a-'a'+10;
} else {
return -1;
}
}
long long strTo(string str,long long radix) {
int size=str.size();
long long num,cur;
num=0;
for(int i=0; i<size; i++) {
cur=todigit(str[i]);
if(cur>=0&&cur<radix) {
num+=(cur*pow(radix,size-i-1));
} else {
return -1;
}
}
return num;
}
int judge(long long radix) { //判断二分是否正确
long long num=0;
if(tag==1) {// tag==1时候
num=strTo(n2, radix);
if(num==num1) {
return 0;
} else if(num>num1||num<-1) {
return 1;
} else {
return -1;
}
} else {
num=strTo(n1, radix);
if(num==num2) {
return 0;
} else if(num>num2||num<-1) {
return 1;
} else {
return -1;
}
}
return 0;
}
int main() {
cin>>n1>>n2>>tag>>radix;
num1=0,num2=0;
// 计算已经给出基数的数的十进制值
if(tag==1) {
num1=strTo(n1,radix);
} else {
num2=strTo(n2,radix);
}
//二分法求解
l=2;
r=10000000000;
ans=-1;
while(l<=r) {
mid=(l+r)/2;
int k=judge(mid);
if(k==0) {
ans=mid;
r=mid-1;
} else if(k==1) {
r=mid-1;
} else {
l=mid+1;
}
}
if(ans==-1) {
cout<<"Impossible";
} else {
cout<<ans;
}
return 0;
}
下面这份代码是搜索下来思路雷同的,而且代码写的比我优美的多,给这个博主一个大大的赞👍。
#include
using namespace std;
long long MAP(long long i){
if(i>='0'&&i<='9') return i-48;
else if (i>='a'&&i<='z') return i-87;
else return -1;
}
long long ToD(string s,long long radix){
long long current, exp, ans=0;
for(int i=s.size()-1;i>=0;i--){
current=MAP(s[i]);
exp=s.size()-i-1;
if(current>=0&¤t<radix){
ans+=current*pow(radix,exp);
}else return -1;
}
return ans;
}
long long upperBound(string s, string s2, long long radix){
long long current, high, max=-1;
for(int i=0;i<s2.size();i++){
current=MAP(s2[i]);
if(max<current)
max=current;
}
max++;
high=max>ToD(s,radix) ? max : ToD(s,radix);
return (high+1);
}
int main(){
string input[2];
long long value[2];
int tag=0;
long long radix;
cin>>input[0]>>input[1]>>tag>>radix;
value[tag-1]=ToD(input[tag-1],radix);
//此处,tag-1代表被指定进制的数,2/tag-1代表的是进制待定的数
//接下来是二分法
//下界:因为我的进制转换函数以及二分过程做了修改,所以我的下界不需要指定,都从2开始即可
//上界:
int low=2;
long long m, curVal, high=upperBound(input[tag-1],input[2/tag-1], radix);
while(low<=high){
m=(low+high)/2;
curVal=ToD(input[2/tag-1],m);//进制待定那个数的当前值
if(curVal==value[tag-1]){
cout<<m;
break;
}else if(curVal>value[tag-1]||curVal<-1) high=m-1;
else low=m+1;
}
if(low>high)
cout<<"Impossible";
return 0;
}