1010 Radix
用时53分钟(这个时间记的可能还不是满的,中间有一些思考时间没计进来……别扶我起来了,我不行了
题意是给出两个数和其中一个数的进制,问另外一个数在什么进制下可以与另外一个数相等,如果任何进制下都不能与另外一个数相等,就输出impossible,如果有多个进制满足条件,输出最小的那一个
这个题我做的着实是 有点难顶
最开始我没仔细想这个题的内部逻辑,我是这么暴力做的:
①计算出已经确定进制的数的真值(使用BigInteger存放)
②计算另外一个数的最低进制(例:如果一个数中出现了字符7,那么它最低是八进制;如果一个数中出现了字符d,那么它最低是14进制),将最低进制记为minRadix
③从minRadix到10000枚举另外一个数的进制,按枚举的这个进制计算这个数的真值(用BigInteger存放),与第一步中计算出来的真值进行比对,如果一样,就输出(因为我是从小到大枚举的,所以这样一定能保证是最小的);如果已经找到10000了还没有找到一个一样的,就输出Impossible
注:这里为什么上限设的是10000呢?因为上限设100000就超时了。。。
这样写的代码如下,结果是测试用例7过不掉
import java.io.BufferedInputStream; import java.math.BigInteger; import java.util.Scanner; import static java.lang.Math.max; public class Main { static BigInteger trueValue(String n, String radix) { //按radix进制计算字符串n的真值是多少(用BigInteger进行存储) char[] array = n.toCharArray(); BigInteger result = new BigInteger("0"); BigInteger rradix = new BigInteger(radix); BigInteger coeff = new BigInteger("1"); for(int i = array.length - 1; i >= 0; i--) { int tt = 0; if(array[i] >= '0' && array[i] <= '9') { tt = array[i] - '0'; } else if(array[i] >= 'a' && array[i] <= 'z') { tt = array[i] - 'a' + 10; } String t = String.valueOf(tt); //当前这一位 BigInteger b = new BigInteger(t).multiply(coeff); coeff = coeff.multiply(rradix); result = result.add(b); } return result; } static int findMinRadix(String s) { //给一个字符串,求它的最低进制-1(这里是当时写的时候没想清楚,所以和最低进制差了1) char[] array = s.toCharArray(); int m = 0; for(int i = 0; i < array.length; i++) { if(array[i] >= '0' && array[i] <= '9') { m = max(m, array[i]-'0'); } else if(array[i] >= 'a' && array[i] <= 'z') { m = max(m, array[i] - 'a' +10); } } return m; } public static void main(String[] args) { Scanner scanner = new Scanner(new BufferedInputStream(System.in)); String s = scanner.nextLine(); String[] array = s.split(" "); String n1 = array[0]; String n2 = array[1]; String tag = array[2]; String radix = array[3]; if(!tag.equals("1")) { //交换 String t = n1; n1 = n2; n2 = t; } BigInteger value1 = trueValue(n1, radix); boolean flag = false; int minRadix = Math.max(findMinRadix(n2),1); for(int i = minRadix+1; i < 10000; i++) { //a digit is less than its radix(不能等于),所以需要从minRadix+1起步 BigInteger value2 = trueValue(n2, String.valueOf(i)); if(value1.compareTo(value2) == 0) { System.out.println(i); flag = true; break; } } if(!flag) { System.out.println("Impossible"); } } }
虽然逻辑简单,但是AC不了,恼火,测试用例7过不了的用时还非常长,合理推测是我的程序没有找到它真正对应的进制,根据我上一版程序的逻辑,问题只能出现在10000这个常数上,他可能是十万进制的。。。想了想可以很容易地给出这样的一组例子:
100000 10 1 10
比如说上述样例就是需要一个十万进制的样例。。。。
已知数据是可以上到十位的,并且每位最多上到z,所以这是不可能枚举到的,于是想到了二分,但是想用二分需要先证明二分的合理性:
我们可以简单理解一下这件事情(理解这部分我用引用标记框起来了,这样比较好看一点):
在以下内容中,我们假设value1是已知进制的数的真值,value2是未知进制数在mid进制下的真值
首先我们需要确定一个前提(这是一个最初步的结论,后面会细化这个结论):那就是随着进制的增大,这个未知进制数的值一定是不会减的(有可能不增,但一定不会减),原因如下:
我们不妨假设这个未知进制的串为c1 c2 c3 …… cn,其中cn是最低位,c1是最高位,当前进制是r进制,那么这个数的真值value2应该等于c1*[r^(n-1)] + c2*[r^(n-2)] + c3*[r^(n-3)] + …… + cn*[(r^0)],在这个式子中,c1 c2 …… cn都是非负数,所以,这个式子整体取值一定会随着r的值增加而不递减(绝大多数情况下都应该是递增的,但也不尽然,我们可以举出c1=c2=c3=……=cn=0 的例子来反驳一定递增的结论,这种情况下不管取多少进制,结果都应该是0)
有了上面这个前提,接下来我们就可以进行分析了:
如果value2 < value1,那么一定是进制小了
类似的,如果value2 > value1,那么一定是进制大了
如果value2 == value1,说明我们找到了一个合适的进制,但是这个进制究竟是不是最小的呢?
不一定!
考虑同一个串(仍然假设为c1 c2 c3 …… cn)的两个进制r1和r2(我们假设r1>r2),在这两个进制下的真值应该分别为:
c1 * [r1 ^ (n-1)] + c2 * [r1 ^ (n-2)] + …… + cn * [r1 ^ 0]
和
c1 * [r2 ^ (n-1)] + c2 * [r2 ^ (n-2)] + …… + cn * [r2 ^ 0]
两者求差,得:
c1 * {[r1 ^ (n-1)] - [r2 ^ (n-1)]} + c2 * {[r1 ^ (n-2)] - [r2 ^ (n-2)]} + …… + cn * {r1 ^ 0 - r2 ^ 0}
其中最后一项的值固定为0
我们可以看到,除了cn以外的所有项,只要它不是0,并且r1和r2不相等,那么这项的乘积一定不是0,并且在我们假定的条件下(r1 > r2),所有的这些项的符号一定都为正;而cn这一项的值固定为0,它不影响符号
什么意思呢?就是说在r1 > r2的前提下,只要你不只有cn这一项,就一定会得到r1进制下的真值大于r2进制下的真值的结论。这句话也可以反过来说,那就是:什么时候value1 == value2,但是这个进制可能不是最小的呢?只有当你的串仅有cn的时候(相当于是串只有一位)!
总结来说:
-
当我们未知进制的串仅有1位的时候,不管这个串是多少进制的,它的真值都是一样的,所以我们可以直接取它的最小的合理进制(即上一版程序第②步中计算出的最低进制minRadix),如果在minRadix进制下它的真值和另外一个数的真值相同,那么minRadix就是答案;否则直接输出impossible
-
在其他情况下(未知进制的串不止1位),我们有未知进制的串的真值随进制radix的值增长而严格递增的结论,因此可以用二分查找的方法来寻找答案(注:初始情况下left应该是minRadix,right应该是已知进制数的真值value1,这里就不赘述做证明了)
到此,终于齐全了,AC代码如下:
import java.io.BufferedInputStream; import java.math.BigInteger; import java.util.Scanner; import static java.lang.Math.max; public class Main { static BigInteger trueValue(String n, BigInteger radix) { //这里我把参数换成了BigInteger类型 char[] array = n.toCharArray(); BigInteger result = new BigInteger("0"); BigInteger coeff = new BigInteger("1"); for(int i = array.length - 1; i >= 0; i--) { int tt = 0; if(array[i] >= '0' && array[i] <= '9') { tt = array[i] - '0'; } else if(array[i] >= 'a' && array[i] <= 'z') { tt = array[i] - 'a' + 10; } String t = String.valueOf(tt); //当前这一位 BigInteger b = new BigInteger(t).multiply(coeff); coeff = coeff.multiply(radix); result = result.add(b); } return result; } static int findMinRadix(String s) { char[] array = s.toCharArray(); int m = 0; for(int i = 0; i < array.length; i++) { if(array[i] >= '0' && array[i] <= '9') { m = max(m, array[i]-'0'); } else if(array[i] >= 'a' && array[i] <= 'z') { m = max(m, array[i] - 'a' +10); } } return m; } public static void main(String[] args) { Scanner scanner = new Scanner(new BufferedInputStream(System.in)); String s = scanner.nextLine(); String[] array = s.split(" "); String n1 = array[0]; String n2 = array[1]; String tag = array[2]; String radix = array[3]; if(!tag.equals("1")) { //交换 String t = n1; n1 = n2; n2 = t; } BigInteger value1 = trueValue(n1, new BigInteger(radix)); int minRadix = Math.max(findMinRadix(n2),1); BigInteger left = new BigInteger(String.valueOf(minRadix)); if(n2.length() == 1) { //如果未知进制的串长度为1,需要特殊处理 if(trueValue(n2, left).compareTo(value1) == 0) { //如果相等,直接输出minRadix(这里加1是因为我的函数返回值少了1) System.out.println(minRadix+1); return; } else { //如果不等,直接Impossible System.out.println("Impossible"); return; } } //value1就是maxRadix的初始值 BigInteger two = new BigInteger("2"); BigInteger right = new BigInteger(value1.toString()); //开始二分查找 while(left.compareTo(right) <= 0) { BigInteger rradix = left.add(right).divide(two); BigInteger value2 = trueValue(n2, rradix); if(value1.compareTo(value2) == 0) { System.out.println(rradix.toString()); return; } else if(value1.compareTo(value2) > 0) { //value1 > value2,说明radix小了 left = rradix.add(BigInteger.ONE); } else { //value2大了,说明radix大了 right = rradix.subtract(BigInteger.ONE); } } BigInteger value2 = trueValue(n2, left); if(value1.compareTo(value2) == 0) { System.out.println(left.toString()); return; } System.out.println("Impossible"); } }
PAT还是挺有意思的~
扶我起来,我还能做!