PAT甲级1010踩坑记录(二分查找)——10测试点未过待更新
题目分析:
首先这题有很多的坑点,我在写完之后依旧还有第10个测试点没有通过,而且代码写的不优美比较冗长勿喷,本篇博客用于记录写这道题的一些注意点
1.关于两个不同进制的数比大小一般采用将两个数都转化为10进制之后比较大小(下面统称已知进制数为N1,未知进制数为N2)
2.虽然两个数都只有10位,且每一位上的数字是从‘0’~‘z’,分别代表0~35,但是这并不意味值这题的进制范围就是2~36,radix完全有可能很大很大到long long,写‘0’~‘z’只是为了让结果计算出来相对小一些,并且迷惑一下
3.由于进制radix有可能很大,故要采用二分查找的方式进行进制的选择判断,否则会超时
4.同时在对每一个二分查询出来的radix进行尝试计算出相应未知进制数N2的10进制表示的过程中有可能溢出long long的范围(会变成负数),需要注意
5.对于已知进制数N1在求出它的10进制表示的时候也有可能溢出long long(这一点比较奇怪,因为如果连已知进制的数N1的10进制表示都是不可记录下来的那如何再去求出N2的10进制二者进行比较呢)
6.对于二分查找N2的进制的时候,N2的下界为N2中数字的最大值+1(例如:10020的最大数字为2,故最低的进制为3进制),而N2的上界则为N1的10进制表示+1(这是一个卡点,这里给出推理的过程)
如果N2的位数超过1位(2位以及以上时),这时候只要1次方位上的数大于等于1,则如果N2的进制为N1的10进制表示+1,则N2永远都大于N1,再大就没有必要了
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 string n1, n2; 6 int tag; 7 long long radix; 8 int len1, len2; 9 int a[15]; 10 int b[15]; 11 long long Min, Max; 12 int judge; 13 14 void init_ab(){ 15 len1 = n1.size(); 16 len2 = n2.size(); 17 int cnt = 0; 18 for(int i = len1-1; i >= 0; i--){ 19 if(n1[i] >= '0' && n1[i] <= '9'){ 20 a[cnt++] = n1[i] - '0'; 21 }else{ 22 a[cnt++] = n1[i] - 'a' + 10; 23 } 24 } 25 //因为未知进制数一定存在b中,所以可以顺便求一下未知进制数的进制下界 26 Min = 0; 27 cnt = 0; 28 for(int i = len2-1; i >= 0; i--){ 29 if(n2[i] >= '0' && n2[i] <= '9'){ 30 b[cnt] = n2[i] - '0'; 31 if(b[cnt] > Min) Min = b[cnt]; 32 cnt++; 33 }else{ 34 b[cnt] = n2[i] - 'a' + 10; 35 if(b[cnt] > Min) Min = b[cnt]; 36 cnt++; 37 } 38 } 39 //Min为未知进制数的所有位中最大数+1 40 Min++; 41 } 42 43 void binary_search(){ 44 //首先需要注意的是 Min和Max的大小要保证Min小于等于Max 45 if(Min > Max){ 46 long long t = Min; 47 Min = Max; 48 Max = t; 49 } 50 long long left = Min; 51 long long right = Max; 52 int flag; 53 while(left <= right){ 54 long long mid = (left + right) / 2; 55 //计算以mid为进制的未知进制数的10进制表示是否和已知进制的10进制相等 56 long long ans = 0; 57 long long base; 58 for(int i = 0; i < len2; i++){ 59 if(i == 0){ 60 base = 1; 61 ans += base * b[i]; 62 }else{ 63 base *= mid; 64 if(base < 0){ 65 flag = 1; 66 break; 67 } 68 ans += base * b[i]; 69 } 70 //判断溢出或者已经大于已知进制数的10进制表示 71 if(ans > Max - 1 || ans < 0){ 72 flag = 1; 73 break; 74 } 75 } 76 if(ans < Max - 1) flag = -1; 77 if(ans == Max - 1) flag = 0; 78 if(flag == 0){ 79 cout<<mid<<endl; 80 break; 81 }else if(flag == -1){ 82 left = mid + 1; 83 }else right = mid - 1; 84 } 85 if(left > right) cout<<"Impossible"<<endl; 86 } 87 88 void cal_Max(){ 89 long long base; 90 //这里设定Max为已知进制数的十进制表示 + 1,且默认不会超过longlong范围,否则题目就太复杂了 91 Max = 0; 92 for(int i = 0; i < len1; i++){ 93 if(i == 0){ 94 base = 1; 95 Max += base * a[i]; 96 }else{ 97 base *= radix; 98 Max += base * a[i]; 99 } 100 if(Max < 0){ //已知进制的数已经溢出 除非n1 n2相等 否则直接impossible? 101 judge = 1; 102 if(n1 == n2) cout<<radix<<endl; 103 else cout<<"Impossible"<<endl; 104 break; 105 } 106 } 107 Max++; 108 } 109 110 int main(){ 111 while(cin>>n1>>n2>>tag>>radix){ 112 judge = 0; 113 if(tag == 2){ //始终将n1用于存放已知进制的数 114 string t = n1; 115 n1 = n2; 116 n2 = t; 117 } 118 //将n1 和 n2两个数的每一位存储到a b数组之中 119 init_ab(); 120 //求出未知进制数的进制上界 121 cal_Max(); 122 if(judge == 0){ 123 //二分查找在上界和下界之间 计算时可能由于进制太大而溢出需要处理 124 binary_search(); 125 } 126 } 127 return 0; 128 }
如果有任何意见请在评论区积极留言