• PAT甲级 1010 Radix 详细题解


    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还是挺有意思的~

    扶我起来,我还能做!

     

  • 相关阅读:
    js中的replace 回车符
    HttpHandler 读取Session
    [原]vs2005 修改新增文件时的默认编码方式
    系统托盘NotifyIcon控件及右键菜单功能
    addAttribute
    gpibCmd
    deviceFactory
    string is
    TitledMotorEntry TitledFrame MotorViewEntry TitleMotorEntry
    IOC port
  • 原文地址:https://www.cnblogs.com/Briddle-ch/p/12616754.html
Copyright © 2020-2023  润新知