腾讯-数字转换机

  1. 数字转换机
时间限制 : (每个 case 2s      空间限制: 128MB
Q 从牛博士那里获得了一个数字转换机,这台数字转换机必须同时输入两个正数 a b ,并且这台数字转换机有一个红色的按钮和一个蓝色的按钮:
当按下了红色按钮,两个数字同时加 1
当按下了蓝色按钮,两个数字同时乘 2
Q 现在手中有四个整数 a b A B ,他希望将输入的两个整数 a b 变成 A B a 对应 A b 对应 B )。因为牛博士允许小 Q 使用数字转换机的时间有限,所以小 Q 希望按动按钮的次数越少越好。请你帮帮小 Q 吧。
输入:
输入包括一行,一行中有四个正整数 a b A B ,( 1<=a b A B<=10^9 )。
 
输出:
如果小 Q 可以完成转换,输出最少需要按动按钮的次数,否则输出 -1
【请注意: javascrip 语言不支持调试,请同学们优先考虑使用其他语言,谢谢】
样例输入: 100  1000  202  2002
样例输出: 2

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class TencentDigitalConversion {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] value = br.readLine().trim().split(" ");
        int a = Integer.parseInt(value[0]);
        int b = Integer.parseInt(value[1]);
        int A = Integer.parseInt(value[2]);
        int B = Integer.parseInt(value[3]);
        int res1 = transfer(a,A);
        int res2 = transfer(b,B);
        if((res1 == res2) && (-1 != res1)){
            System.out.println(res1);
        }else{
            System.out.println(-1);
        }
        int res3 = transfer2(a,A);
        int res4 = transfer2(b,B);
        if((res3 == res4) && (-1 != res3)){
            System.out.println(res3);
        }else{
            System.out.println(-1);
        }
        int res5 = transfer3(a,A);
        int res6 = transfer3(b,B);
        if((res5 == res6) && (-1 != res5)){
            System.out.println(res5);
        }else{
            System.out.println(-1);
        }
    }

    /**
     * 递归 定义状态
     * transfer(before,after)表示从数字before转化到数字after最少转化次数
     * 定义状态转移方程
     * 自顶而下分析,最后一步转化无非两种情况,前一步结果*2或者+1,那么前一次结果为after/2或者after-1。
     * 最后一步经历的转化次数无非是transfer(before,after/2)或者transfer(before,after-1)。
     * 显然可得,取最小的transfer(before,after) = min(transfer(before,after/2),transfer(before,after-1))+1
     * 递归边界最终会有两种情况,一种是before刚好与after相同,说明这是可以转换。一种是before>after,说明不能完成转化。
     *
     * @param before 转化前
     * @param after 转化后
     * @return
     */

    private static int transfer(int before, int after) {
        if (before == after)
            return 0;
        if (before > after)
            return -1;
        int res1 = transfer(before,after/2);
        int res2 = transfer(before,after-1);
        if(-1 == res1 && -1 == res2){
            return -1;
        }else if(-1 == res1){
            return res2+1;
        }else if(-1 == res2){
            return res1+1;
        }else {
            return Math.min(res1,res2)+1;
        }
    }

    static int transfer2(int before,int after){
        int[] buf = new int[after+1];
        for(int i = 0;i<buf.length;i++){
            buf[i] = -1;
        }
        return transfer2(before,after,buf);
    }

    /**
     * memorandum 备忘录法
     * 定义一个数组,数组的下标表示after,数组值表示从数字before转化到数字after最少转化次数。
     * 数组需要初始化为无效值-1,用于判断是否已经计算过
     * @param before
     * @param after
     * @param buf 数组
     */
    private static int transfer2(int before,int after,int[] buf) {
        if (before == after)
            return 0;
        if (before > after)
            return -1;
        if(-1 != buf[after])
            return buf[after];
        int res1 = transfer2(before,after/2,buf);
        int res2 = transfer2(before,after-1,buf);
        if(-1 == res1 && -1 == res2){
            return -1;
        }else if(-1 == res1){
            buf[after] = res2+1;
        }else if(-1 == res2){
            buf[after] = res1+1;
        }else{
            buf[after] = Math.min(res1,res2)+1;
        }
        return buf[after];
    }

    /**
     *  表格法
     *  用自底而上的迭代代替自顶而下的递归
     *  数组表示含义与备忘录法一致的,虽然会有一定空间浪费,时间复杂度,已经从O(2^n)降低到O(n)
     *  通常有时间限制的DP,优先使用表格法,备忘录法在一些情况会比表格法快,基本不使用递归法。
     *  使用回溯法可以把表格法和备忘录法获得的存储中间值,转化成获得最优解具体的操作,
     *  例如本题中,小Q究竟是依次按了哪些按钮获得last值。
     * @param before
     * @param after
     * @return
     */
    private static int transfer3(int before,int after) {
        int[] buf = new int[after+1];
        for(int i= 0;i<buf.length;i++){
            buf[i] = -1;
        }
        for(int i=before+1;i<after+1;i++){
            if(i/2 >= before && i-1 >= before){
                buf[i] = Math.min(buf[i/2+1],buf[i-1])+1;//可能有问题
            } else if(i/2 >= before){
                buf[i] = buf[i/2]+1;
            } else if(i-1 >= before){
                buf[i] = buf[i-1]+1;
            } else {
                buf[i] = -1;
            }
        }
        return buf[after];
    }
}


参考:https://www.jianshu.com/p/131874a8c7f3

猜你喜欢

转载自blog.csdn.net/u010996775/article/details/79279428