寻友之旅|青训营笔记

90 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 15 天
当青训营遇上码上掘金

题意

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

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

思路

  • 首先看一眼数据范围(100000),所以要考虑使用时间复杂度为O(n)或者O(nlogn)的算法。脑子里就蹦出要用DP(动态规划)。 通过分析题意一共有三种状态的转移,因为考虑更优的情况,小青不会考虑往后(0的方向)走很多步然后坐公交车或者再走回去,这样步数只会更大。同时如果小青的位置大于小码的位置,他只能往回走,而不是坐车,毕竟小青的位置大于小码的位置时,一定是小青坐公交到达的(走过去再走回来有点蠢蠢)。

状态转移

  • all i ->> [i-1]->[i] ->> dp[i] = min(dp[i], dp[i - 1])
  • i % 2 == 0 ->> [x]->[2*x] ->> dp[i] = min(dp[i], dp[i / 2] + 1)
  • i % 2 == 1 ->> [x]->[2*x]->[2*x+1] ->> dp[i] = min(dp[i], dp[i / 2] + 1)

统计答案

  • 最后的最小步数就等于从前面来的最小步数和先超过小码一点再往回走的步数。

代码

本来先用c++写的,但是码上掘金那的c++不知道为什么用不了,也可能是我不会用,所以就直接从dev复制过来,改用go写

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 20, INF = 0x3f3f3f3f;
int dp[N], s, t;

int main(){
    memset(dp, 0x3f, sizeof(dp));
    scanf("%d%d", &s, &t);
    dp[s] = 0;
    // 往后走
    for(int i = 0; i < s; i++) dp[i] = s - i;
    // 往前走
    int ans = INF;
    for(int i = s + 1; i < N; i++){
        // 从前一格到当前格
        dp[i] = min(dp[i - 1] + 1, dp[i]);
        int res = INF;
        if(i % 2 == 0) res = dp[i / 2] + 1; // 从 i/2 的位置来
        else res = dp[i / 2] + 2; // i/2 -> i/2 + 1 -> i
        dp[i] = min(dp[i], res);
        
        if(i >= t){
            ans = min(ans, dp[i] + (i - t));
        }
    }
    printf("%d\n", ans);
    return 0;
}