【问题标题】:Is is possible to move from (a,b) to (c,d)是否可以从 (a,b) 移动到 (c,d)
【发布时间】:2019-10-29 01:21:47
【问题描述】:

问题是输出是否可以从给定点(a,b)移动到目标(c,d)

我们仅限于正坐标

以下两个动作是可能的

(a,b) -> (a+b,b)
(a,b) -> (a,b+a)

例如,(1,1)(5,4)True 您可以执行以下操作:使用第二步 3 次,(1,1) -> (1,2) -> (1,3) -> (1,4) 使用第一步 1 次 (1,4) -> (5,4)

我想出了以下递归方法

def move(a,b,c,d):
    if a==c and b==d:
        return True
    elif a>c or b>d:
        return False
    else:
        ans = False
        if a < c:
            if move(a+b,b,c,d):
                return True
        if b < d:
            if move(a,b+a,c,d):
                return True
    return False

a) 我的解决方案是否涵盖所有可能的情况。由于我没有测试用例,因此我无法确定验证,但我认为我确实考虑了所有因素。

b) 我的解决方案的时间复杂度是多少?我认为它是指数级的,但不能肯定地说。

c) 是否有更好的解决方案(就时间复杂度而言)。我们可以使用动态规划吗?

感谢您的任何意见。

【问题讨论】:

  • 我认为你可以通过elif a&gt;c or b&gt;d:而不是and来优化它
  • @ScottSauyet 是的,抱歉。固定
  • 我认为您的解决方案不能涵盖所有情况,那么:(1, -1) -> (0, -1)
  • 所以问题是:数字必须是正数吗?如果是这样,我认为有一个更快的解决方案。
  • @ScottSauyet 是否涉及 GCD?

标签: algorithm recursion time-complexity dynamic-programming


【解决方案1】:

答案:

一个。是的,它涵盖了所有情况。

b.它的复杂性是指数级的,因为它试图从每个状态到达所有剩余状态。

c。是的,您可以通过记忆 dp[a][b] 来使用动态编程;

初始化dp[][]全部为-1;

def move(a,b,c,d):
    // memoizing is here.
    if dp[a][b] != -1
        return dp[a][b];
    dp[a][b] = INF; // INF = 100000000;
    if a==c and b==d:
        return dp[a][b] = True
    elif a>c and b>d:
        return dp[a][b] = False
    else:
        ans = False
        if a < c:
            if move(a+b,b,c,d):
                return dp[a][b] = True
        if b < d:
            if move(a,b+a,c,d):
                return dp[a][b] = True
    return dp[a][b] = False

如果使用动态规划,复杂度会降低到 O(c*d)

【讨论】:

    【解决方案2】:

    如果所有数字都必须是正数,那么我相信有一个更快的解决方案。

    试图找出我们是否可以从(a, b)(14, 31),我们可以注意到只有正数到达(14, 31) 的唯一方法是将第二条规则应用于(14, 17)。到达(14, 17) 的唯一方法是将第二条规则应用于(14, 3)。到达(14, 3) 的唯一方法是将第一条规则应用于(11, 3)(11, 3) 的唯一方法是将第一条规则应用于(8, 3),依此类推。所以唯一能达到(14, 31)的值是

    (14, 31) # Final
    (14, 17) # Rule 2
    (14, 3)  # Rule 2
    (11, 3)  # Rule 1
    (8, 3)   # Rule 1
    (5, 3)   # Rule 1
    (2, 3)   # Rule 1
    (2, 1)   # Rule 2
    (1, 1)   # Rule 1
    

    所以算法非常简单。循环(c, d),如果c &gt; d(c - d, d) 替换它,如果c &lt; d(c, d - c) 替换它,当你击中匹配时停止,当c == d 时,当c &lt; ad &lt; b 时。

    Paul Hankin 在评论中描述的一个变体是O(log n),尽管我不打算证明这一点。此版本为O(n),其中ncd 中的较大者。连续的斐波那契数可能会采取最多的步骤。

    当然,如果你可以有负数,这一切都是没有意义的,因为应用于(-17, 31) 的第一条规则也会产生(14, 31),而你又回到了指数。

    【讨论】:

    • 在最坏的情况下似乎 O(n),例如从 (1, 1)(1, n)。无论如何都是不错的简单解决方案
    • 这不是 log(n),因为例如 (n,1) 到 (1,1) 需要 n-1 步。但是,如果你一次性应用所有重复应用的规则:(c, d) -> (c%d, d) 或 (c, d%c)。当您跳过目标时,您必须小心注意,但这并不难合并。
    • @PaulHankin:是的,当我考虑时间复杂度时,我确实想到了这一点,但决定这个问题应该只得到更简单的答案。然后我忘了重申复杂性。更新以明确这一点。
    • @ScottSauyet 说你的决赛是 (10,10)。您可以通过 (0,10) 和 (10,0) 达到此目的。在这种情况下,您如何决定应用哪条规则?
    • @hsnd:但是数字必须是正数,所以零就出来了。所以你可以停在(10, 10)。这就是为什么我说我们在c = d 时停止。如果我们需要包含零,只需稍作修改即可处理c = d 的一个奇怪情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-27
    • 1970-01-01
    • 1970-01-01
    • 2022-11-09
    • 1970-01-01
    • 2018-09-14
    • 2013-06-23
    相关资源
    最近更新 更多