Самый быстрый алгоритм для перехода через массив

Начните с массива A положительных чисел. Начните с индекса 0. Из индекса я вы можете перейти к индексу я + 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), который, я думаю, в порядке, но, вероятно, есть более быстрый способ?

Ответы

Ответ 1

Поскольку вы можете выбрать любой x из [1, A [i]], я думаю, есть довольно простое решение:

начинаться с 0:

выберите следующий доступный элемент, из которого вы можете достичь более дальнего элемента. i.e выберите i, которые максимизируют я + A [i + x] для x в [1, A [i]]

пока вы не дойдете до конца списка.


Пример:

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

начать с 0

от 0 вы можете добраться до 1 или до 2:

  • от 1 вы можете добраться до 4
  • от 2 вы можете добраться до 3

поэтому max (0 + A [0 + x]) для я = 1

выбрал 1 от 1 вы можете добраться до 2 3 4:

  • от 4 вы можете добраться до 7
  • от 3 вы можете добраться до 5
  • от 2 вы можете добраться до 3

поэтому max (1 + A [1 + x]) для я = 4

выбрал 4

вы можете достичь 7

остановки

the resulting list is : 

0,1,4,7

Как объясняется в моих комментариях, я думаю, что это O (N), потому что из я вы достигаете я + x + 1 по крайней мере в 2 * x операциях.


"Псевдо"

Вы начинаете с 0 (это оптимально)

то вы выбираете i, которые максимизируют (0 + A [0 + x]) (т.е. максимизируем достижимость для следующего элемента)

из этого я вы можете достичь любого другого элемента, доступного из всех других элементов, достижимых с 0 (это длинное предложение, но это означает: кто может делать больше, может делать меньше, поэтому, если я не оптимален, это как хороший как оптимальный)

Итак, я оптимально

то после этого шаг за шагом это рассуждение доказывает оптимальность метода.

Если кто-то знает, как сформулировать это более математически, не стесняйтесь его обновлять.

Ответ 3

Используйте свою основную идею, но начинайте с начала и вы можете получить O(n).

Цель состоит в том, чтобы сделать последовательность (A_i1, A_i2, ..., A_ik, ...) такую, что

  • позиции 0,1,2,...,ik могут быть достигнуты в k или меньше шагов

  • позиции i(k-1)+1, i(k-1)+2, ..., ik не могут быть достигнуты менее чем за k шагов

Базовый блок прост:

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) }

Ответ 4

Я пойду против потока и скажу вам, что ваш алгоритм "совершенен".

Он использует динамическое программирование в своей самой чистой форме, и его сложность не так уж плоха. В этом смысле я бы сказал, что, скорее всего, это будет ожидаемо от вас на собеседовании.

Если у вас есть привязка к записям (например, A [i] <= C (N)), то его сложность равна O (N * max (C (N), N)). Например, если все записи меньше K, это O (N).

Использование алгоритма Дейкстры (или, как правило, сокращение проблемы до самой короткой проблемы пути) является разумным, но я оцениваю его за чистым решением DP, поскольку алгоритмы графа сложны (и это могло бы иметь неприятные последствия при интервью, если вас спросили о их).

Обратите внимание, что Dijkstra будет O (N C (N) + N log N) вместо (N вершин и N C (N) ребер). Поэтому, в зависимости от C, вы либо строго лучше, либо равны по сложности.

Ответ 5

Вы могли бы сформулировать его как алгоритм графа (действительно, какая проблема не может быть?). Пусть позиции в массиве - вершины, а возможные адресаты имеют ребро из каждой вершины. В вашем примере вершина 0 будет иметь ребра до 1 и 2, а вершина 1 будет иметь ребра до 2, 3, 4 и 5.

Существует несколько эффективных алгоритмов поиска графа. Например, Dijkstra O(|E| + |V|log|V|), а A * - O(log h*), что лучше, если вы можете придумать хорошую эвристику.

Ответ 6

Решение динамического программирования:

отслеживать для каждого элемента наименьшее количество шагов, которые вы можете получить там и откуда вы пришли. затем просто пройдите через массив и для каждого элемента обновите доступные позиции (от я + 1 до я + 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])).. или немного меньше, вам не нужно выходить за пределы массива.

Ответ 7

Вы можете преобразовать массив в граф и найти кратчайший путь. Вот как должно работать преобразование из массива в граф.

Каждый элемент массива является node. И, основываясь на значении в элементе массива, край, нарисованный между node и другими индексами (узлами), к которым мы можем перейти. Как только мы получим этот график, мы можем найти кратчайший путь, который лучше, чем O (n ^ 2).

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

Ответ 8

Мой наивный подход - начиная с самого начала, делая дыхание сначала по всем путям (дочерние узлы A [i + 1].. A [i + n]), сохраняя найденные пути для некоторого массива, а затем получаем кратчайший пути. Конечно, все индексы я + n > length (A) отбрасываются. Таким образом, верхняя граница O (n * min (n, max (A [i = 0..n])) + n) - in должна быть на практике менее квадратичной.

Ответ 9

Здесь небольшая модификация ответа Рики Бобби, которую я покажу, будет оптимальной:

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) = ith node моего пути имеет "охват" не меньше i-го node любого оптимального пути

где досягаемость определяется как номер наивысшего node, с которым вы можете перейти от этого node или + бесконечности, если вы можете пройти мимо конца массива

P (0) очевидно.

предположим, что P (k) верно при k >= 0

теперь рассмотрим (k + 1) th node в пути, созданном моим алгоритмом. Поскольку мой алгоритм выбрал node k, чтобы он имел, по крайней мере, тот же доступ, что и для оптимального пути node k, набор узлов, который может быть (k + 1) th node для моего алгоритма, равен надмножество одного и того же для любого оптимального пути. Поскольку мой алгоритм выбирает node с наибольшим достижением, из этого следует, что P (K + 1) истинно.

по индукции, P (k) истинно для всех k (до размера созданного пути).

так как мой алгоритм завершится, как только конец массива достигнет цели, и это произойдет не позже, чем для любого оптимального пути, из этого следует, что путь, созданный моим алгоритмом, является оптимальным.

доказательство оптимальности: каждая ячейка массива рассматривается не более одного раза, поэтому O (n), которая асимптотически оптимальна. Я не думаю, что можно разработать алгоритм, который проверяет меньшее количество ячеек в каждом случае.

Ответ 10

Мой метод: Создайте массив reqSteps, чтобы сохранить количество перемещений, которые принимает вход для выхода.
Начните с конца массива.
Проверьте, может ли вход [i] выйти из массива сам по себе, если да введите 1 в minStep, если нет, сохраните минимум последовательных входных значений [i] + 1. Результат minSteps [0];
Верхний метод не работает для ввода {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];
}

}