PAT甲级 1010 Radix (25分)

1010 Radix (25分)

题目链接: PAT A 1010
在这里插入图片描述在这里插入图片描述
这道题难度比较大,而且测试点还非常多,我debug就花了一天时间,尽量为大家讲解的清楚一些吧。
思路分析:这题给两个数和其中一个数的进制,要找出另外一个数是什么进制的时候使得这两个数相等。注意题目里数位的范围是0-35,但是这个进制不一定小于36,而是从2到int最大值甚至是long long最大值的任意一个数,比如1可以是二进制数也可以是十进制数。为了方便起见,如果tag等于2,那么我们就交换n1和n2,使得已知进制的数是n1,未知进制的数是n2。然后就可以将两个数都转换成十进制去比较他们的大小,注意不可以暴力枚举所有的进制,因为这样数据量过大,会超时,这就需要用到二分法找n2的进制:二分法的下界是n2的所有数位中最大值加一(八进制是由0-7构成的,十进制是由0-9构成的,一个数最小的进制就是数位最大的值加一),上界就是n1转换为十进制之后的值(有可能小于下界)和下界中的大者(因为如果在大,那n2转换成十进制就一定大于那个已知进制数的十进制了。当n2只有一位时,其大小与基数无关,例如:只要基数大于8,那么8这个数不管是在16进制下还是20进制下,都是8(10进制下),因为任何基数的0次方都是1嘛;当N2有两位或以上时,倒数第二位最小是1,那么基数为n1时,这个数就必然会大于等于n1,所以再大就没有必要比较了)。经过测试 :本题数据默认保证n1(已知进制的数)转换成十进制的数时不超过long long(题目中没有说),因此只要对n2在转换成十进制时判断是否溢出(只要在转换过程中某步小于0就是溢出,比如int范围是2的31次幂-1,那么如果int a = 2的31次幂,那么输出这个a就会显示一个负数)。由于n1不超过long long,所以如果n2溢出,那么就是超过了long long的范围,就肯定大于n1了。
1.首先定义一个INF代表long long的最大范围,值为2的63次幂-1
2.写一个转换成十进制的函数,当字符串是n1的时候if(ans < 0 || ans > t)是不会执行的,n2时候才有可能执行,此时ans小于0代表溢出,ans大于t代表超过n1了,都返回-1
3.写一个比较函数,作用是比较n1转换成十进制和n2转换成十进制数的大小
4.二分查找函数,不断改变n2进制去比较n1转换成十进制和n2转换成十进制数的大小,如果相等了就说明此时的进制就是我们要找的进制。如果没有找到就返回-1代表没有找到
5.找最大数位的函数,输入的是字符串n2,返回最大数位+1即二分查找的下界
6.难点基本已经解释清楚,其余部分见注释

AC代码:

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
long long INF = pow(2, 63) - 1; //long long范围内最大的数 
long long change(string s, long long radix, long long t) { //将字符串转换为十进制 
	long long ans = 0; //计算s代表的十进制数 
	for(int i = 0; i < s.length(); i++) {
		if(s[i] >= '0' && s[i] <= '9') //数位是0-9 
			ans = ans * radix + s[i] - '0'; 
		else //数位是a-z,代表10-35 
			ans = ans * radix + s[i] - 'a' + 10;
		if(ans < 0 || ans > t) //ans小于0代表溢出,ans大于t代表超过了n1表示的十进制数 
			return -1;
	}
	return ans;
}
int compare(string s, long long radix, long long t) { //n2的十进制与t比较 
	long long num = change(s, radix, t); //计算n2代表的十进制 
	if(num < 0) //这说明n2代表的十进制一定大于n1代表的十进制 
		return 1;
	if(num > t)
		return 1;
	else if(num == t)
		return 0;
	else
		return -1;
}
long long binarysearch(string s, long long left, long long right, long long t) { //二分查找 
	long long mid;
	while(left <= right) {
		mid = (left + right) / 2;
		int flag = compare(s, mid, t);
		if(flag == 0) //n1代表的十进制与n2代表的十进制相等 
			return mid;
		else if(flag < 0) //n1的大,要增大n2的进制范围 
			left = mid + 1;
		else //n1的小,减小n2进制范围 
			right = mid - 1;
	}
	return -1; //没找到使他们相等的进制 
}
int findmax(string s) { //找最大数位,用于计算下界 
	int ans = 0;
	for(int i = 0; i < s.length(); i++) {
		if(s[i] >= '0' && s[i] <= '9') {
			if(s[i] - '0' > ans)
				ans = s[i] - '0';
		}
		else {
			if(s[i] - 'a' + 10 > ans)
				ans = s[i] - 'a' + 10;
		}
	}
	return ans + 1; //返回最大数位加1代表下界 
}
int main() {
	string a, b, temp; //a代表n1,b代表n2 
	int tag, radix;
	cin >> a >> b >> tag >> radix;
	if(tag == 2) { //如果给定的是n2的radix,方便起见,交换n1,n2,使得已知的永远是n1的进制 
		temp = a;
		a = b;
		b = temp;
	}
	long long t = change(a, radix, INF); //计算n1代表的十进制数 
	long long low = findmax(b); //下界 
	long long high = max(low, t); //上界 
	long long ans = binarysearch(b, low, high, t); //这里的t就是我们刚计算出的t,即n1代表的十进制数
	if(ans == -1) //没找到 
		cout << "Impossible";
	else
		cout << ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值