当青训营遇上码上掘金之寻友之旅

371 阅读2分钟

主题 3:寻友之旅

小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)

请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)

作者:青训营官方账号
链接:juejin.cn/post/717498…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

思路

当小青和小码在一条直线上,小青要去找小码,此题应该用动态规划来解决。

我们假设dp[i] 为小青到地点 i 所花费的最短时间。

如果小码在小青的后面(K < N),那么小青只能步行前往,即小青从任意节点X移动到节点X-1,直到找到小码,这一种情况比较好理解,dp[i] = dp[i-1] + 1;

若小码在小青的前面(K > N),那么这种情况即可以步行前往,也可以坐公交前往。通过数学归纳法发现当 i <= n/2 + n 时,全程步行前往比较快,当i > n/2 + n 时就需要坐几次公交快些。其实我们可以举一个例子,当n为10,k为11-15时,不管怎样,都步行快些,而当k为16时,如果全步行,花费6分钟,如果先步行到8花费2分钟,再坐公交一次到16,总共花费3分钟,更省时间。

所以发现一条公式当 i > n/2 + n 时,dp[i] = dp[i/2] + i%2 + 1;当i <= n/2 + n时,dp[i] = dp[i-1] + 1。

初始化:dp[n] = 0; dp[n+1] = 1; dp[2n] = 1;当i<n时,dp[i] = n-i;

由此可写出:

public class Test3 {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int k = scan.nextInt();
        System.out.println(method(n,k));
    }

    //dp[i]为小青到地点n所需要的最短时间
    // dp[n] = 0; dp[n+1] = 1 ; dp[2n] = 1 ; 小于i的位置都为步行
    //如果 i <= n/2 + n
    //dp[i] = dp[i-1] + 1
    //其他情况下
    // dp[i] = dp[i/2] + i%2 + 1;
    private static int method(int n, int k) {
        //小码在小青的前面
        if (k <= n){
            return n-k;
        }
        //小青在小码的后面
        int[] dp = new int[k+1];
        int count = 1;
        for (int i = n-1; i >= 0; i--) {
            dp[i] = count++;
        }
        dp[n] = 0;
        dp[n+1] = 1;
        for (int i = n+2; i <= k; i++) {
            if ( i - n <= n/2){
                dp[i] =dp[i-1] + 1;
            }else {
                dp[i] = dp[i/2] + i%2 + 1;
            }
        }
        return dp[k];
    }
}

码上掘金代码链接:code.juejin.cn/pen/7182473…

但是我不知道怎么用Scanner输入,所以测试用例就写死在上面了。

代码仅供参考,可能还有忽略的地方,欢迎大佬们评价。