回到编程比赛的一段时间,我遇到了一个令人费解的问题,从那以后一直困扰着我。虽然我不记得它,但我会尽力重现它:
杰克从数字线上的0开始,向任一方向跳跃一个单位。他所做的每次连续跳跃都比之前的1个单位长,并且可以在任一方向上进行。编写一个带有数字的程序,并返回Jack为达到该数字而进行的最小跳跃次数。
如果这不是一个好问题,或者标题被认为具有误导性,我会事先道歉。
答案 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
示例中,过冲为4
(15-11=4
),因此我们只需将+2
跳转到-2
跳转,这就是“隐藏”4超调的地方。但是,事情并非总是如此简单:12
可以通过-1-2+3+4-5+6+7
与7
联系:它需要5
个步骤,而不是预测的12
。基本观察是过冲必须甚至,否则不会出现过冲/ 2步。以下是我们如何找到5
15
,这使我们进入3
5
。 因此,对于12,我们尝试15
步骤,产生3
并超过21
。然后我们尝试六个步骤,产生9
和7
的过冲。最后,我们尝试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
,这样就可以满足给定整数n
的解,因为a
必须是一个正整数。让我们注意一些事情:
n
为偶数,则k
或k+1
必须可被4整除;即,k = 0 or 3 mod 4
。n
为奇数,那么k
和k+1
都不能被4整除;即,k = 1 or 2 mod 4
。这将处理有关所有整数数字的部分。要处理关于k
和a
为正面的部分,我们需要规定
k >= floor(sqrt(2n))
呼。现在,举个例子:
说n = 7.然后我们知道k
和k+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,反转序列中的任何数字组合产生一个奇数,所以差值是偶数。