当青训营遇上码上掘金
主题 3:寻友之旅
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
**请帮助小青通知小码,小青最快到达时间是多久?**
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)
题目挺有意思的,简单说一下我的思路,如有错误还请指出。
首先可以注意到题目中向左走只有一种方法,而向右走可以乘公交,也可以步行,因此我们可以对K<=N的情况特判,直接返回N-K。
对于K>N的情况,首先需要注意的是,如果需要步行的距离很大(至少两步),则先步行再乘公交的时间,一定小于先乘公交再步行的情况,因为步行移动的距离会被公交的乘法放大。举个例子辅助理解,如果N=1000, K=2003,则最优解应该是先向右移动一格,再乘公交,再右移一格,总时间为3分钟;而如果先乘公交再右移三格,总时间为4分钟。第二个需要注意的点是,如果当前位置为x,终点为2*x+1,则显然应该先乘公交再右移,而不是右移乘公交左移。换句话说,如果x为奇数,则x的上一步应该是x/2,而不是x/2+1。
接下来进入讨论的重点。对于BFS算法,如果直接从起点向终点走,可以预见的一个问题是,需要记录的位置太多,影响效率。由于终点的位置比起点大,我们可以从终点向起点走,记录终点到达起点需要的步数。这样一来,乘公交的操作就变成了从x到达x/2,且x为偶数。这里就可以用到我们上面的两条结论:先步行再乘公交(反过来就是先乘公交再步行),以及x -> x/2(在x为奇数的时候,只左移乘公交,不右移乘公交)。
我们用一个while循环表示这个过程,用step记录过程中的步数。结束的条件是,乘公交无法带来更高的收益,即K<=N(此时乘公交一定会远离N),while循环的条件就是K>N。最后的结果是,从K到N的步数+当前步数,或者从2*K到N的步数+当前步数-1(无论x是奇数还是偶数,最后一步都是乘公交),二者的较小值,即min(step + N - K, step - 1 + K * 2 - N)。
Java代码如下:
public static int findFriend(int N, int K) {
if (K <= N) {
return N - K;
}
int step = 0;
while (K > N) {
step += 1 + K % 2;
K /= 2;
}
return Math.min(step + N - K, step - 1 + K * 2 - N);
}
最后出一道附加题:如果起点恒定为1,你有什么快速计算步数的方法吗?(提示:二进制)