1010 Radix(二分)

1010 Radix

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.

Input Specification:

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.

Output Specification:

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.

Sample Input 1:

6 110 1 10

Sample Output 1:

2

Sample Input 2:

1 ab 1 2

Sample Output 2:

Impossible

1、大致题意

  • 给出两个数 N 1 N1 N1 N 2 N2 N2 ,这两个数的长度不超过10位。
  • 再给出一个标志位,
    • 如果是1,则后面的数是 N 1 N1 N1 的进制
    • 如果是2,则后面的数是 N 2 N2 N2 的进制
  • 要求求出另外一个数为多少进制时跟所给出的数相等,并且输出该进制,如果不存在相等,则输出Impossible。

2、基本思路

用两个字符串来接收 N 1 N1 N1 N 2 N2 N2 ,便于进行处理。用两个整形来接收标志位和进制位,将所给出进制的数转化成十进制,未给出进制位的数,使用二分法,找出最小的进制位。

3、解题过程

3.1 初次提交 (18/25)

3.1.1 基数下界

基数下界这个问题其实很容易想到,比如说下面这个用例:

输入:1 1z1 1 10
输出:Impossible

z 不可能出现在10进制中。所以我在 judge 中加入了 3.2 中提到的部分来加以判断。

有了上面所述的思路,马上写出了第一份代码

#include<iostream>
#include<math.h>
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)。

在这里插入图片描述

3.2 找到最小进制问题(提高1分,19/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<iostream>
#include<math.h>
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;
}

在这里插入图片描述

很高兴多对了一分。

3.3 变量问题

既然这道题的基数可以很大,那么int类型势必是不太够的,本来考虑到既然 pow()函数返回值类型是 double,那我也用 double不就行了(毕竟 double 超级大啊!!!!),但是用 double 在暴力遍历法中用是可以的,但是在使用二分法的时候明显会出现小数部分,这就会给最后的判断带来麻烦。

这个时候只能退而求其次,用 long long ,说是退而求其次,但事实证明,long long 在这道题中是够用的,而且后来在查阅资料的时候发现,大家遇事不决都会用 long long 这个整型数扛把子

数据类型取值范围
int2147483648~2147483647
long2147483648~2147483647
long long-9223372036854775808 ~ 9223372036854775807

3.4 数值溢出(24/25)

当然这里的数值溢出指的不是指已知基数的字符串的数值会溢出,而是指在二分搜索过程中未知基数的字符串的值会在数制转换之后溢出为负数。这样一来,就会导致原本应该是当前基数过大,应该使基数上界下移,但变为负数之后,判断条件以为基数过小了,就会调整使基数下界上移,这与我们二分搜索的初衷背道而驰。

注意啊,我下面这份代码里面,二分的上界 r 开了100,拿了(24/25)

#include<iostream>
#include<math.h>
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;
}

在这里插入图片描述

3.5 二分法的上下限

3.5.1 基数上界

原本我以为进制的范围是 2 ~ 36,毕竟用来代替数字的字符也就指定了 36 个,所以我在二分的上界 r 开了100 ,当时感觉自己已经保守了。但是当很多测试用例跑不通时,我开始思考基数的上界是不是不止 36 ,比如下面这个用例:

输入:42949672 10 1 10
输出:42949672
3.5.2 第一份AC代码

二分的上界 r 开了10000000000 就AC了。

#include<iostream>
#include<math.h>
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;
}

在这里插入图片描述

3.6 第二份AC代码

考虑到第一份AC代码重复的部分太多,耦合度实在太高(虽然感觉ACM的代码不讲究美观),重新修改了下面这一份代码,和上面的第一份AC代码没有本质区别。

#include<iostream>
#include<math.h>
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;
}

3.7 网上优秀代码

下面这份代码是搜索下来思路雷同的,而且代码写的比我优美的多,给这个博主一个大大的赞👍。

附上链接

#include <bits/stdc++.h>
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&&current<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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值