[青训营 X 码上掘金] 主题三

75 阅读4分钟

当青训营遇上码上掘金

一、题目概述

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

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

二、思路分析

2.1 题目分析

本题的输入参数:

  • 小青的地点 N(整型,范围是 0N100,0000 \leq N \leq 100,000
  • 小码的地点 K(整型,范围是 0K100,0000 \leq K \leq 100,000

输出结果:小青到小码家的最短时间(以分钟为单位,即为整型,结果范围应当 0x100,0000 \leq x \leq 100,000

2.2 情况分析

1 N>KN > K

即小青的位置坐标比小码大。根据题目,公交车不可以往后走。这种情况下,只能通过步行方式前往。所花费的时间即为 NKN -K

2 N==KN == K

小青与小码的位置坐标重合。这种情况下小青不需要任何方式前往,所花费的时间为 00

3 N<KN < K

这种情况下有多种方式可以使用。

直接步行

结果为 KNK -N。但是分析可知。这种情况几乎不可能是最优。即便是最优,也会被以下几种方式覆盖。

乘公交车前往离小码位置最近的点然后步行

image.png

乘公交车坐过一站后步行返回

image.png

乘公交车到离小码坐标一半最近的点并步行至此再坐公交

image.png

2.3 细节分析

  • 若小青位置为0,乘坐公交只能原地踏步,因此必须至少先步行到1再乘坐公交
  • 若乘坐公交的位置大于50000,再乘坐公交会大于最大坐标。因此认为此时乘坐公交一分钟可到达最大坐标位置
  • 题目中数据处理均为整数,因此不存在到达小数位置坐标的情况
  • 若输入不合法应当提示错误信息

三、代码详解

本题采用 Java 完成。

3.1 数据输入

Scanner in = new Scanner(System.in);

System.out.println("请输入小青地点 N:");
int N = in.nextInt();
if( N<0 || N>LIMIT ) {
    System.out.println("小青地点输入有误");
    return;
}

System.out.println("请输入小码地点K");
int K = in.nextInt();
if( K<0 || K>LIMIT ) {
    System.out.println("小码地点输入有误");
    return;
}

新建一个 Scanner 对象用于数据的输入。对于输入小青和小码的坐标进行判断,如果不是在题目给定的范围内,提示错误并结束程序。

3.2 KNK \leq N 的简单情况

if( N==K ) {    // 两人在同一点
    System.out.println("0 min!已经到啦!");
} else if( N>K ) {  // 只能步行
    System.out.println("还要 " + (N-K) + " min! 走过来啦!");
}

这种情况下要么步行到达要么直接坐标点重合,情况简单。

3.3 N<KN < K 分别计算几种方案的时间

首先处理特殊情况:小青坐标点为0。这个时候先步行至坐标点 1,再进行处理。

if( N==0 ) {    // 起点在 0 不能坐公交
    N = N + 1;
    temp = temp + 1;
    T0 += 1;
    t0 += 1;
}

“跨越”相同的距离,乘坐公交比步行更快。因此先乘坐公交到离小码坐标点最近的“小点”(坐标值比小码小)。

while( 2*N<K ) {    // 公交快速接近
    t0 += 1;
    N *= 2;
}

直接走路,所花费的时间即两点的距离。

// 方案一 直接走路
int t1 = K - N + t0;

走到小码坐标点一半的位置,再乘坐公交前往。

// 方案二 后退至可乘坐公交
int t2;
if( K%2==0 ) {      // 地点 K 恰好能坐公交到
    t2 = N - K/2 + 1;
} else {
    t2 = N + 1 - (K+1)/2 + 1;
}
t2 += t0;

多坐一站公交,走路返回小码的位置。

// 方案三 坐过站返回
int t3;
t3 = (2*N < LIMIT ? 2*N : LIMIT ) - K + t0 + 1;

先移动到小码坐标点一半的位置再坐公交。

int t4;
while( temp*2<K/2 ) {
    T0 += 1;
    temp *= 2;
}
int dis1, dis2;
if( K%2==0 ) {
    dis1 = K/2 - temp;
    dis2 = temp * 2 - K/2;
} else {
    dis1 = (K-1)/2 - temp;
    dis2 = 2*temp - (K+1)/2;
}
if( dis1 < dis2 ) {
    t4 = T0 + Math.abs(dis1) + 1;
} else {
    t4 = T0 + 1 + Math.abs(dis2)+ 1;
}

对这四种方案进行比较,取最小值并输出。

f( t1<=t2 && t1<=t3 && t1<=t4 ) {
    System.out.println("还要 " + t1 +" min");
} else if( t2<=t1 && t2<=t3 && t2<=t4 ) {
    System.out.println("还要 " + t2 +" min");
} else if( t3<=t1 && t3<=t2 && t3<=t4 ) {
    System.out.println("还要 " + t3 +" min");
} else if( t4<=t1 && t4<=t2 && t4<=t3 ) {
    System.out.println("还要 " + t4 +" min");
}

四、测试结果

4.1 一般情况

输入:N=500, K=200
输出:300

image.png

输入:N=450,K-450
输出:0

image.png

输入:N=200,K=500
输出:51

image.png

4.2 小青坐标点为0

输入:N=0,K=5000
输出:465

image.png

输入:N=0,K=100000
输出:18

image.png

五、反思总结

本题一开始在解决时忽略了一些边界条件,导致出错。 目前的版本几种方案仍然存在这重叠的情况,尚可进行优化。

源代码链接为:主题三源代码

欢迎大家批评指正!