寻友之旅 |「青训营 X 码上掘金」主题创作活动

74 阅读3分钟

当青训营遇上码上掘金

主题介绍

寻友之旅

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

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

思路实现

广度优先搜索BFS

这是一个比较典型的BFS问题。小青在去找小码的路上,每一个位置X都有三种选择:前进到X+1,后退到X-1,前进到X×2,所以BFS思路就很好理解了,从小青的位置n开始,每次BFS三个位置 n-1,n+1,n×2 三个点,然后判断三个点是否合法(即大于等于0,小于等于100 000),同时为了不重复计算搜索过的点,用visited记录是否搜索过的情况。如果位置合法且该位置还没有搜索过,就标记该位置,记录步数,压入队列即可;直到到达k(即visited记录位置k已搜索)

Java实现

// 小青在每个位置都有三种方式行进,保证每种方式都考虑过后选择最优,所以可以开一个队列,记录BFS遍历的“点”
Queue<Integer> q = new LinkedList<>();
// 防止重复计算同一个“点”,声明一个visited,访问过就标记为true,不再重复计算
boolean[] visited = new boolean[100001];
// time[i]表示小青到位置i所需要最短时间
int[] time = new int[100001];

public int shortestTime(int n, int k) {
    if(n >= k) {
        // 小青在前方,然而公交车不能后退,小青只能选择每1min步行后退
        return n-k;
    }
    Arrays.fill(visited, false);
    visited[n] = true;
    time[n] = 0;
    q.offer(n);
    while(q.size() > 0 && visited[k] == false) {
        int cur = q.peek(); // 小青当前位置cur
        q.poll();
        // 三种行进方式
        if(cur+1 <= 100000 && cur+1 >= 0 && visited[cur+1] == false) {
            visited[cur + 1] = true;
            time[cur + 1] = time[cur] + 1;
            q.offer(cur + 1);
        }
        if(cur-1 <= 100000 && cur-1 >= 0 && visited[cur-1] == false) {
            visited[cur - 1] = true;
            time[cur - 1] = time[cur] + 1;
            q.offer(cur - 1);
        }
        if(cur*2 <= 100000 && cur*2 >= 0 && visited[cur*2] == false) {
            visited[cur*2] = true;
            time[cur*2] = time[cur] + 1;
            q.offer(cur*2);
        }
    }
    return time[k];
}
动态规划DP

动态规划三部曲

  1. 确定dp数组及下标的含义

    dp[i]表示到位置i需要的最短时间。

  2. 确定递推公式

    考虑恢复到先前状态有3种情况: 1.向左移一位 2.向右移一位 3.向左闪现,坐标为X/2

    第3种情况只有偶数时才能合法。所以递推式要分奇偶判断:

    1. i为偶数: dp[i] = min(dp[i], dp[i-1]+1, dp[i/2]+1, dp[i/2+1]+2)
    2. i为奇数: dp[i] = min(dp[i], dp[i-1]+1, dp[(i-1)/2]+2, dp[(i+1)/2]+2)
  3. dp数组初始化 初始化时不能将dp[i]初始化为+INF,要初始化为abs(n-i),即最坏情况下所需要的时间。

Java实现

int[] dp = new int[100001];

public int shortestTime(int n, int k) {
    // dp数组初始化
    for(int i = 0; i <= k; i++) {
        dp[i] = Math.abs(n-i);
    }
    for(int i = n+1; i <= k; i++) {
        if(i % 2 == 0)
            dp[i] = Math.max(dp[i], Math.max(dp[i-1]+1, Math.max(dp[i/2]+1, dp[i/2+1]+2));
        else
            dp[i] = Math.max(dp[i], Math.max(dp[i-1]+1, Math.max(dp[(i-1)/2]+2, dp[i+1)/2]+2));
    }
    return dp[k];
}