当青训营遇上码上掘金之主题三(Bfs解法)

272 阅读3分钟

主题 3:寻友之旅

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

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

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

一些思考
N>KN > K 的情况很显然只能通过步行向后走从 NN 到达 KK;重点考虑 N<KN < K 的情况: 如果 NNKK 之间相距较远,选择通过公交快速缩短距离,但往往不能正好到达节点 KK,这会存在好几种可能情况(不一定都是最优选择):

  • 先步行,再一直坐公交正好到达。例如 N=5,K=16N = 5, K = 16,路线为 5步行4公交8公交165 \stackrel{步行} \longrightarrow 4 \stackrel{公交} \longrightarrow 8 \stackrel{公交} \longrightarrow 16
  • 先坐公交,接近后再换步行。例如 N=5,K=19N = 5, K = 19,路线为 5公交10公交20步行195\stackrel{公交} \longrightarrow 10 \stackrel{公交} \longrightarrow 20 \stackrel{步行} \longrightarrow 19
  • 先坐公交,中间换步行,再坐公交正好到达。例如 N=5,K=38N = 5, K = 38,路线为 5公交10公交20步行19公交385 \stackrel{公交} \longrightarrow 10 \stackrel{公交} \longrightarrow 20 \stackrel{步行} \longrightarrow 19 \stackrel{公交} \longrightarrow 38
  • 还可能有先步行,再坐公交,最后再步行到达等情况。

Bfs 求解
由于 N, KN,\ K 的范围不大,所以可以通过 bfs 搜索来解决此题,用一个数组 distdist 记录答案,dist[i]dist[i] 代表从节点 NN 到达节点 ii 所需的最短时间。Bfs 搜索可以确保考虑到所有情况:比如我当前搜索到了节点 uu(从队列首部取出了 uu),那么我接下来要么往后走一步,要么往前走一步,要么坐公交。如果能更新 distdist 数组就更新,另外如果该节点没有入队过则将其压入队列。这里的 visvis 数组可以保证不会有节点重复入队。
代码实现思路大体为为:每次从队列头部取出节点 uu,如果其能更新 u1, u+1, 2uu-1,\ u+1,\ 2*u,则更新 distdist 数组,并将相应节点入队。

#include <iostream>
#include <queue>
#include <vector>
const int INF_MAX = 1000000007;

int Bfs(int N, int K) {
    std::queue<int> que;
    // N < K 时,可能存在通过公交到达 x(x > K),再步行向后走最优的情况,因此数组需要多开辟一些空间
    // 这种情况下只可能会是 x = K + 1,因为如果 x > K + 1,坐公交前先步行向后走一步更优 
    int M = std::max(N, K) + 10;
    // dist[i] 代表从节点 N 到达节点 i 最少所需时间,初始化为很大的数(无穷大)
    std::vector<int> dist(M + 1, INF_MAX); 
    std::vector<bool> vis(M + 1, false); // 标记是否已经入队过

    // 将起始点压入队列
    dist[N] = 0;
    que.push(N);
    vis[N] = true;
    while(!que.empty()) {
        int u = que.front();
        que.pop();
        // 步行向后走一步
        if( u > 0 && dist[u-1] > dist[u] + 1 ) {
            dist[u - 1] = dist[u] + 1 ;
            if( !vis[u - 1] ) {
                que.push(u - 1);
                vis[u - 1] = true;
            }
        }
        // 步行向前走一步
        if( u < M && dist[u+1] > dist[u] + 1 ) {
            dist[u + 1] = dist[u] + 1;
            if( !vis[u + 1] ) {
                que.push(u + 1);
                vis[u + 1] = true;
            }
        }
        // 公交
        if( 2 * u <= M && dist[2 * u] > dist[u] + 1 ) {
            dist[2 * u] = dist[u] + 1;
            if( !vis[2 * u] ) {
                que.push(2 * u);
                vis[2 * u] = true;
            }
        }
    }
    return dist[K];
}

int main() {
    int N, K;
    std::cin >> N >> K;

    int ans = Bfs(N, K);
    std::cout << ans;
}