主题 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输入,所以测试用例就写死在上面了。
代码仅供参考,可能还有忽略的地方,欢迎大佬们评价。