决定'跳跃杰克'的算法

时间:2013-03-14 10:40:50

标签: algorithm

回到编程比赛的一段时间,我遇到了一个令人费解的问题,从那以后一直困扰着我。虽然我不记得它,但我会尽力重现它:

  

杰克从数字线上的0开始,向任一方向跳跃一个单位。他所做的每次连续跳跃都比之前的1个单位长,并且可以在任一方向上进行。编写一个带有数字的程序,并返回Jack为达到该数字而进行的最小跳跃次数。

如果这不是一个好问题,或者标题被认为具有误导性,我会事先道歉。

6 个答案:

答案 0 :(得分:8)

对于任意数量的跳跃,人们可以轻松计算出千斤顶可以行进的最大正距离。翻转正跳跃的极性总计任何特定值 k 将导致千斤顶比其他情况下的最低2k计数结束。对于任何最大距离 t ,以及相同奇偶校验的任何非负 n (即使 t 是偶数;如果 t则为奇数是奇数)小于或等于该距离,可以找到总计 n 的跳跃组合。因此,人们不必担心树木,背包或任何其他类似的东西 - 只是一些跳跃是否足够,以及它是否会产生正确的“奇偶校验”。

答案 1 :(得分:7)

我想详细说明@ supercat的正确和快速解决方案,并描述一种算法,除了计算这样一个总和的长度外,还计算最小长度和。

算法

找到最小整数k,使得t_k:= 1 + 2 + 3 + ... + k> = | n |和t_k具有与| n |相同的奇偶校验。 然后以系统的方式将t_k的加号的符号翻转为总n。

以下是详细信息。 注意t_k = k(k + 1)/ 2,三角形数。 设置t_k = | n |并且求解k给出(-1 + sqrt(1 + 8 | n |))/ 2的上限。 所以k等于上限或1或2加上它,这三个数字中的哪一个具有与n相同的奇偶性并且是最小的。 这里我们使用的事实是,对于任何正整数t,s,三个连续三角数的集合{t,t + s,t + s +(s + 1)}包含偶数和奇数。 (只需检查t和s的所有四种奇偶校验可能性。)

要找到n的最小长度和,首先计算d:=(t_k - n)/ 2。 因为t_k> = | n |并且t_k和n具有相同的奇偶校验,d位于集合{0,1,2,...,t_k}中。 现在重复减去:d = a_k(k)+ r_k,r_k = a_ {k-1}(k-1)+ r_ {k-1},...,r_2 = a_1(1)+ r_1,选择每个a_i最大值为{0,1}。 通过下面的引理,r_1 = 0。 所以d = sum_ {i = 1} ^ k a_i i。 因此,n = t_k-2d = sum_ {i = 1} ^ ki - sum_ {i = 1} ^ k 2a_i i = sum_ {i = 1} ^ k(1 - 2a_i)i和1 - 2a_i位于{-1 ,1}。 所以序列b_i:= 1 - 2a_i是一条路径,并且通过k的最小值,b_i是一条最小路径。

算法示例

考虑目标数n = 12。根据算法3,k的可能性是5,6或7.对应的t_k值是15,21和28.由于28是具有与n相同奇偶性的最小值,我们看到k = 7 。所以d =(t_k - n)/ 2 = 8,我们根据算法写成1 + 7。因此,到12的最短路径是-1 + 2 + 3 + 4 + 5 + 6-7。

我说一个最短路径,因为最短路径通常不是唯一的。 例如,1 + 2 -3 + 4 - 5 + 6 + 7也有效。

算法正确性

引理:让A_k = {0,1,2,...,t_k}。 然后一个数字位于A_k中,当且仅当它可以表示为{0,1}中的某个序列a_i的和sum_ {i = 1} ^ k a_i i。

证明:通过感应k。 首先,0 = sum_ {i = 1} ^ 0 1,空和。 现在假设结果适用于所有k-1> = 0并且假设数字d位于A_k中。 重复减去:d = a_k(k)+ r_k,r_k = a_ {k-1}(k-1)+ r_ {k-1},...,在{0,1中最大选择每个a_i = 0或1并且当第一个r_j位于A_j中时停止某些j&lt; ķ。 然后通过归纳假设,对于{0,1}中的一些b_i,r_j = sum_ {i = 0} ^ j b_i i。 然后根据需要d = r_j + sum_ {i = j + 1} ^ k a_k i。 相反,对于{0,1}中的a_i,和s s = = sum_ {i = 1} ^ k a_i i满足0 <= s&lt; = sum_ {i} ^ k i = t_k,因此s位于A_k中。

算法时间复杂度

假设算术运算是恒定时间,从n计算k需要O(1)时间,因此从n计算最小路径的长度。 然后花费O(k)时间来找到d的最小长度和,然后花费O(k)时间来使用该和来产生n的最小长度和。 所以O(k)= O(sqrt(n))时间全部。

答案 2 :(得分:2)

n 连续整数之和的公式为n(n+1)/2。例如1+2+3+4=10 = 4*5/2=10。这是达到目标号码所需的最小步骤数。但可能会出现过冲。假设目标是11。四次跳转会让您10(我们只计算了这一点),但5会将您带到5*6/2=15。现在我们只注意到在11的情况下,当步长为2时我们会跳回来,并且我们正确地到达11。我们稍后会更详细地处理过冲。回到如何计算跳跃的数量。我们的公式为n(n+1)/2 = x,其中x是目标数字。 quadratic equation告诉我们解决方法是

(-1+/-sqrt(-1+8x)))/2

(-1-/+(sqrt(9x))/2

否定的“版本”将始终产生一个虚数,这与此无关,所以我们有

(sqrt(9x) + 1)/2

取这个数字的上限,你需要初始跳跃次数。

过冲有点复杂。在我们的到达11示例中,过冲为415-11=4),因此我们只需将+2跳转到-2跳转,这就是“隐藏”4超调的地方。但是,事情并非总是如此简单:12可以通过-1-2+3+4-5+6+77联系:它需要5个步骤,而不是预测的12。基本观察是过冲必须甚至,否则不会出现过冲/ 2步。以下是我们如何找到5

的步数
  1. 使用上述算法,我们发现最小步数为15,这使我们进入3
  2. 计算过冲:我们有5
  3. 如果过冲是奇数(在这种情况下),请尝试下一个步骤并返回步骤2,直到找到均匀过冲。这是您的步骤数
  4. 因此,对于12,我们尝试15步骤,产生3并超过21。然后我们尝试六个步骤,产生97的过冲。最后,我们尝试28步骤,产生16并超越{{1}}。这是我们的最小步骤数。但是,这可以通过公式来计算。

答案 3 :(得分:1)

您可以将Jack的进度建模为二叉树,其中左侧节点表示向后跳跃,右侧节点表示向前跳跃。

每个节点的值是杰克的当前位置。

节点的深度与当前的跳跃长度相对应。

编辑 - 您无法修剪与树中较高节点具有相同值的节点,因为其子节点的值将不同,因为它的深度不同。

<击> 为了防止搜索空间过快增长,您需要积极地修剪任何值为前一节点重复的节点。

此外,可以修剪根下面的整个左子树,因为所有值都是右子树中相应值的否定。例如:

右子树: 0 + 1 + 2 + 3 - 4 = 2

左子树中的镜像: 0 - 1 - 2 - 3 + 4 = -2

幸运的是,树似乎会产生大量重复。例如,在深度= 7时,而不是32个节点(64/2,因为我们只处理正确的子树),似乎只有6个不同的节点:

4 = 0 + 1 + 2 + 3 + 4 - 5 + 6 - 7
14 = 0 + 1 + 2 + 3 + 4 + 5 + 6 - 7
16 = 0 + 1 + 2 + 3 + 4 + 5 - 6 + 7 
18 = 0 + 1 + 2 + 3 + 4 - 5 + 6 + 7
20 = 0 + 1 + 2 + 3 - 4 + 5 + 6 + 7
28 = 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7

32种可能组合中的所有其余组合似乎都是已经在树上方的正数,或者是镜像左侧子树的负数。

所以我会先进行广度搜索,直到找到我要找的号码。

答案 4 :(得分:0)

问题如下:

找出最小的k,其中第一个k正自然的总和使得$ k(k + 1)/ 2 = a + b $,其中$ n = a - b $

我们有一个方程组:

  • k(k+1)/2 = a + b
  • n = a - b
  • a, b, k是正整数

有三个未知数,只有两个方程。我们可以结合起来得到满足的目标方程:

  • k(k + 1)+ 2n = 4a

我们需要找到最小的正k,这样就可以满足给定整数n的解,因为a必须是一个正整数。让我们注意一些事情:

  1. 如果n为偶数,则kk+1必须可被4整除;即,k = 0 or 3 mod 4
  2. 如果n为奇数,那么kk+1都不能被4整除;即,k = 1 or 2 mod 4
  3. 这将处理有关所有整数数字的部分。要处理关于ka为正面的部分,我们需要规定

    • k >= floor(sqrt(2n))

    呼。现在,举个例子:

    说n = 7.然后我们知道kk+1都不能被4整除。同样,我们知道k >= 4。我们可能会立即跳过k = 4,因为我们知道k不能被4整除。我们可能会尝试k = 5;我们进入我们的系统:

    n = 7
    k = 5
    a = 11
    b = 4
    

    这些数字都有效,所以我们有一个有效的解决方案。我们选择解决方案的方式是首先必须找到最小k的解决方案。如果你关心,你甚至可以重建杰克使用的跳跃序列。在这种情况下,很容易:杰克向右跳11,向左跳4。向左跳4的唯一方法是向左跳1和向左跳3。所以杰克跳得如下:

    ----------J------N---
    ---------J-------N--- -1
    -----------J-----N--- +2
    --------J--------N--- -3
    ------------J----N--- +4
    -----------------J--- +5
    

    但是,对于您的问题,一旦找到有效的k,您就完成了。在找到有效值之前,您不需要尝试k的许多值。

答案 5 :(得分:0)

我们正在寻找所需数量j,以便从1到j的总和大于目标数和相同的奇偶校验。

这是python 2.7中的一个简单的工作代码:

def numberOfJumps(n):
    n = abs(n)
    j = 0
    while True:
        s = j*(j+1)/2
        if s >= n and not (s - n)%2:
            break
        j += 1
    return j

for i in range(10):
    print i, numberOfJumps(i)

问题可以简化为正数,因为-n和n需要相同数量的跳转。我们只需要为序列中的每个数字向相反方向跳跃。

然后,为了达到n,我们必须确保只有正跳跃的总和达到n或更多。

我们还必须确保总和的奇偶校验与目标数相同,因为向负方向跳跃会在总和上产生均匀差异,因此不会改变其奇偶校验。

假设你跳1 + 2 + 3 + 4 + 5 = 15,反转序列中的任何数字组合产生一个奇数,所以差值是偶数。