「青训营 X 码上掘金」寻友之旅实现即思路

77 阅读3分钟

当青训营遇上码上掘金

题目

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

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

解题思路

看到题目第一思路是用贪心法解决这个问题,但是贪心法有一个缺点,它是一种只考虑眼前情况的策略。由于没有考虑到长远的情况,贪心法并不总是能给出最佳答案。

第二个想法是动态规划,在这道题中,小青只有两种交通方式:步行和公交。其中步行可以进一步或者退一步,而公交只能进不能退,并且每次只能前进2的倍数。观察这些信息,我们可以发现: 当小青处于位置i时,小青到小码家所需要的最短时间f(i)仅取决于f(i-1)f(i+1)f(i/2),因此我们可以得到关系式:

f(i)=min{f(i1),f(i+1),f(i/2)}+1f(i) = min\{f(i-1), f(i+1), f(i/2)\} + 1

在这里我定义了一个数组,用于记录小青处于路程中的某个中间位置时,所需的最短时间。通过一步步的累积,最终确定小青到小码家所需的最短时间。

最终代码实现如下:

# 寻友之旅
def find_friend() -> None:
    """
    本实验小青是起点,小码是终点。
    以小码的位置K建立数组,小青的位置N作为在数组中的一个数,数组记为arr。
    使用动态规划思想,关系式为:
        dp[i] = min(dp[i-1], dp[i//2], dp[i+1]) + 1
    其中dp定义为从小青到小码的步数
    """
    while True:
        # N: 小青的位置
        N = int(input("请输入小青的位置 N: "))
        # K: 小码的位置
        K = int(input("请输入小码的位置 K: "))
        if N < 0 or K > 100000 or N > K:
            print("请输入程序要求的参数")
            continue
        else:
            break
    
    if N == K:
        return 0
    else:
        # 将数组延长10个单位,给步行方式后退一步留下空间
        dp = [0 for _ in range(K + 11)]
        dp[N] = 0
        for i in range(N + 1, K + 1):
            if i % 2 == 0 and dp[i+1] > 0:
                dp[i] = min(dp[i-1], dp[i//2], dp[i+1]) + 1
            elif dp[i+1] > 0:
                dp[i] = min(dp[i-1], dp[i+1]) + 1
            elif i % 2 == 0:
                dp[i] = min(dp[i-1], dp[i//2]) + 1
            else:
                dp[i] = dp[i-1] + 1
    print(f"最少花费{dp[K]}分钟")


if __name__ == "__main__":
    find_friend()

同时放上码上掘金的代码。注意:码上掘金的编译器不支持输入,即input()


总结

对于每一个动态规划问题,我们需要首先定义一个数组,用于记录动态规划的中间结果。在这一个过程中,我们需要明确这一个数组需要记录的数据是什么。然后我们需要归纳出动态规划问题具体的问题关系式,接下来就是根据这一个得出的关系式设计代码,得到最后的结果。

参考链接为:什么是动态规划(Dynamic Programming)?动态规划的意义是什么?