题目链接:PAT【甲级】1010
题目简述:给定形式为N1 N2 tag radix
的输入用例,N1和N2是两串数字或字母(a-z,即对应十进制为10-35)组成的“数字串”,tag是标记后面radix(基数)是哪一个数字串的基数,为1就是第一个串。
让找出一个基数,来使得未给出基数和给出基数的数字串相等。如果有多个这样的基数,找到最小的那个。(其实这个条件限制很多余,不可能会出现多个不同的基数使得二者相等,毕竟是单调递增的)
#include<bits/stdc++.h>
using namespace std;
//将每次更新基数后的数字串变成十进制数
long long getResult(string& s, long long radix){
long long temp = 0;
for (int i = 0; i < s.size();i++){
if(s[i] >= '0' && s[i] <= '9')
temp = temp * radix + (s[i] - '0');
else
temp = temp * radix + (s[i] - 'a' + 10);
}
return temp;
}
int main(){
string s1, s2;
long long num, radix;
long long dest = 0;//记录目标数
cin >> s1 >> s2 >> num >> radix;
if(num == 2)
swap(s1, s2);
dest = getResult(s1, radix);
//找出基数下限
radix = 0;
for (int i = 0; i < s2.size();i++){
if(s2[i] >= '0' && s2[i] <= '9')
radix = max((long long)s2[i] - '0' + 1, radix);
else
radix = max((long long)s2[i] - 'a' + 11, radix);
}
//进行二分查找最小的基数
long long radix_h = dest;
long long radix_l = radix;
long long temp;
while(radix_l < radix_h){
radix = (radix_l + radix_h) >> 1;
temp = getResult(s2, radix);
if(temp < 0 || temp >= dest){
radix_h = radix;
}else{
radix_l = radix + 1;
}
}
if(getResult(s2, radix_l) == dest)
cout << radix_l;
else
cout << "Impossible";
return 0;
}
最开始我去做这道题目的时候,是直接从基数下限radix,开始累加上去,直到相等退出。很可惜这次拿到24分,第七个测试点超时,后来我就去限制if(radix > 1000000) break;
这样发现结果错误,再将上限加几个0发现超时,直到我限制到一定范围,才发现测试点那里执行用时300ms,题目限制400ms。我才意识到原来是基数太大了,累加上去很费时间。
很自然想到二分,但是上限是谁?这里犯蠢了,看了别人的博客才知道是dest,很显然的事情。。。。。
接下来直接二分就可以了,二分的时候也要注意
if(temp < 0 || temp >= dest){
radix_h = radix;
}else{
radix_l = radix + 1;
}
这个判断,temp < 0
是溢出的情况,太大了,要去处理一下,不然又死循环了。
其实上面代码的后面还可以这样写,因为不可能有多个基数:
while(radix_l <= radix_h){
radix = (radix_l + radix_h) >> 1;
temp = getResult(s2, radix);
if(temp < 0 || temp > dest){
radix_h = radix - 1;
}else if(temp < dest){
radix_l = radix + 1;
}else{
break;
}
}
if(getResult(s2, radix) == dest)
cout << radix;
else
cout << "Impossible";
看出不同了么?其实这就是写二分时要注意的一个点,它有不同的写法自然也有不同的作用!!!
具体的道理其实是**左闭右开**
还是**左闭右闭**
,以此来判断区间边界如何变化,与此对应的还有查询目标位置在哪里的判断。