跳过数组的最快算法

时间:2011-09-08 20:06:42

标签: arrays algorithm

从正数的数组A开始。从索引i开始,你可以移动索引i + x得到任何x< = A [i]。目标是找到到达阵列末尾所需的最小移动次数。

以下是一个例子:

{ 2 , 4 , 1 , 2 , 3 , 2 , 4 , 2} 

如果你在每次行动中总是尽可能地走,那么这就是你得到的:

0 , 2 , 3 , 5 , 7

这需要4步。但是你可以通过这种方式更快地完成它

0 , 1 , 4 , 7

这只需要3个动​​作。

我想到了这一点并做了我想到的第一件事,但在思考了几天之后,我仍然不知道如何做得更好。

这是我的想法。从数组的末尾开始,跟踪从某个位置到结尾的最小移动次数。所以对于这个例子,moves[7] = 0因为它已经结束了。然后moves[6] = 1,因为它需要一个动作才能结束。我的公式是

moves[i] = 1 + min(moves[i+1], moves[i+2], ... , moves[i+A[i]])

当我开始时,我知道移动的次数。

所以这是O(n ^ 2)我觉得没问题,但可能还有更快的方法吗?

10 个答案:

答案 0 :(得分:13)

既然您可以选择[1,A [i]]中的任何x,我想有一个非常简单的解决方案:

从0开始:

选择下一个可到达的元素,您可以从该元素到达更远的元素。 即选择i在[1,A [i]]

中最大化i + A [i + x]

直到你到达列表的末尾。


示例:

{2,4,1,2,3,2,4,2}

从0开始

从0开始,你可以得到1或2:

  • 从1你可以到4
  • 从2你可以到3

因此max(0 + A [0 + x])适用于i = 1

选择1 从1你可以到2 3 4:

  • 从4你可以到7
  • 从3你可以到5
  • 从2你可以到3

因此max(1 + A [1 + x])适用于i = 4

选择4

你可以达到7

停止

the resulting list is : 

0,1,4,7

正如我在评论中所解释的那样,我认为它是O(N),因为从i开始,你至少在2 * x的操作中达到i + x + 1。


'伪'证明

你从0开始(这是最佳的)

然后你选择i最大化(0 + A [0 + x])(即最大化下一个元素的可达性)

从那个i你可以到达任何其他元素,这些元素可以从0到达的所有其他元素到达(这是一个很长的句子,但它意味着:谁可以做得更多,可以做得更少,因此,如果我不是最优的,那就是好最好)

所以我是最优的

然后逐步推导出这种推理,证明了该方法的最优性。

如果有人知道如何以数学方式表达,请随时更新。

答案 1 :(得分:4)

将数字数组视为图形,然后问题等同于Shortest Path Problem,可以使用Dijkstra's algorithmO(|E|+|V|log|V|)时间内解决。

E = 的数字。

V =数字#。

答案 2 :(得分:2)

使用您的基本想法,但从头开始,您可以获得O(n)

目标是制作一个序列(A_i1, A_i2, ..., A_ik, ...),以便

  1. 位置0,1,2,...,ik可以k或更少步骤

  2. 位置i(k-1)+1, i(k-1)+2, ..., ik无法在少于k步骤中达到

  3. 基本案例很简单:

    i0 = 0
    i1 = A[0]
    

    和归纳部分并不太复杂:

    i(k+2) = max { A_(ik+1) + ik , A_(ik+1) + ik+1, ..., A_(i(k+1)) + i(k+1) }
    

答案 3 :(得分:2)

我会反对这个流程并告诉你你的算法是“完美的”。

它以最干净的形式使用dynamic programming,其复杂性并不是那么糟糕。从这个意义上讲,我会说这可能是你在面试中所期待的。

如果您对条目有界(例如A [i]< = C(N)),则其复杂度为O(N * max(C(N),N))。例如,如果所有条目都小于K,则为O(N)。

使用Dijkstra的算法(或者更普遍地将问题简化为最短路径问题)是明智的,但我将其排在干净的DP解决方案之后,因为图算法很复杂(如果你被问及有关,它可能适得其反它们)。

注意Dijkstra将改为O(N C(N)+ N log N)(N个顶点和N C(N)个边缘)。因此,根据C,您要么在复杂性方面要么严格要求也要相同。

答案 4 :(得分:1)

您可以将其表示为图算法(实际上,问题不能是什么?)。让数组中的位置为顶点,并且可能的目标从每个顶点开始具有边。在您的示例中,顶点0的边缘为1和2,而顶点1的边缘为2,3,4和5.

有几种有效的图搜索算法。例如,Dijkstra是O(|E| + |V|log|V|),而A *是O(log h*),如果你能想出一个好的启发式,那就更好了。

答案 5 :(得分:0)

动态编程解决方案:

跟踪每个元素您可以到达的最小步骤数以及您来自哪里。然后只需简单地遍历数组,并为每个元素更新可用的下一个位置(从i + 1到i + a [i])。

{ 2 , 4 , 1 , 2 , 3 , 2 , 4 , 2} 
  0

{ 2 , 4 , 1 , 2 , 3 , 2 , 4 , 2} 
  0   1   1 (num of steps)
      0   0 (source)
  ^         (current position)
{ 2 , 4 , 1 , 2 , 3 , 2 , 4 , 2} 
  0   1   1   2   2   2
      0   0   1   1   1
      ^
{ 2 , 4 , 1 , 2 , 3 , 2 , 4 , 2} 
  0   1   1   2   2   2
          ^
etc...

这是O(n + sum(a [i]))..或者稍微少一点,你不必超出数组的边界。

答案 6 :(得分:0)

您可以将数组转换为图形并找到最短路径。以下是从数组到图形的转换应该如何工作。

每个数组元素都是一个节点。并且,基于数组元素中的值,我们可以跳转到节点与其他索引(节点)之间绘制的边。一旦我们得到这个图,我们就可以找到最短路径,这比O(n ^ 2)好。

http://i.imgur.com/Ih3UP.png

答案 7 :(得分:0)

我的天真的方法 - 从一开始,通过所有路径先做呼吸(子节点是A [i + 1] .. A [i + n]),保存找到的路径你的某些数组,然后得到最短的路径。当然所有索引i + n>长度(A)被丢弃。所以它的上限是O(n * min(n,max(A [i = 0..n]))+ n) - 在实践中应该小于二次。

答案 8 :(得分:0)

以下是Ricky Bobby答案的略微修改,我将证明这是最佳的:

find_shortest_path(A):
    path := [0]
    last := 0
    max_reachable = 0

    while A[last] + last < length(A) :  
        next_hop := x such that max_reachable < x <= A[last] + last and last + A[x] is maximum
        push(path, x)
        max_reachable = A[last] + last
        last := x

    return path

正确性证明: 我将使用归纳我的算法创建的路径的节点。

我要显示的属性是P(i)=我路径的第i个节点的“到达”不小于任何最佳路径的第i个节点

其中,覆盖范围定义为可以从该节点跳转到的最高节点数,如果可以超出数组末尾,则为+无穷大

P(0)很明显。

假设对于k> = 0

,P(k)为真

现在考虑我的算法创建的路径中的第(k + 1)个节点。由于我的算法选择节点k使其至少具有与最优路径节点k相同的范围,因此可能是我的算法的第(k + 1)节点的节点集是同一个节点的超集。任何最佳路径。由于我的算法选择具有最大范围的节点,因此P(K + 1)为真。

通过归纳,P(k)对于所有k都是正确的(直到创建的路径的大小)。

因为我的算法会在数组结束时立即结束,并且这不会晚于任何最佳路径,因此我的算法创建的路径是最佳的。

最优性证明: 数组的每个单元最多被考虑一次,因此它是O(n),它是渐近最优的。我不认为设计一种算法可以在每种情况下检查更少的单元格。

答案 9 :(得分:0)

我的方法: 创建一个数组reqSteps来存储输入转义所需的移动次数 从数组的末尾开始。
检查input [i]是否可以自行转义数组,如果是,则在minSteps中输入1,如果不是,则存储连续输入[i]值+ 1的最小值。 结果是minSteps [0];
top方法不适用于输入{10,3,1,1,1,1,1,1,1,1,2,1};
这将给出9作为答案 正确答案是2.

public static void arrayHop()
{
        int[] input = { 10, 3, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1 };
        int length = input.length;
        int answer = calcArrayHop(input, length);

}

public static int calcArrayHop(int[] input, int length) {
    int minSteps;
    int[] reqSteps = new int[length];
    for(int i=0;i<length;i++)
        reqSteps[i]=Integer.MAX_VALUE;
    int nextStep;

    for (int i = length - 1; i >= 0; i--) {
        int minsteps = Integer.MAX_VALUE;
        if (i + input[i] >= length) {
            reqSteps[i] = 1;
        } else
        {
            for (int j = i+1; j <= (i + input[i]); j++) {
                if(j>input.length-1)
                    break;
                if (reqSteps[j] < minsteps)
                    minsteps = reqSteps[j];
            }
        reqSteps[i] = minsteps+1;
        }


    }

    return reqSteps[0];
}

}