#javascript #arrays #algorithm #dynamic-programming #graph-algorithm
#javascript #массивы #алгоритм #динамическое программирование #граф-алгоритм
Вопрос:
Задан двумерный массив чисел.
Найдите «змеиный путь», тогда как:
- В змеином пути должен быть элемент в каждой строке, а в двух соседних строках элементы находятся в соседних столбцах
- Сумма «змеиного пути» должна быть максимальной и делиться на 3 (сами элементы необязательно должны быть кратны 3)
Я пытался решить этот вопрос целую вечность. То, что я пробовал до сих пор, это:
Я подсчитал, что существует n ^ 3 потенциальных змеиных пути, а длина каждого змеиного пути равна n, а затем я бы просто перебрал n ^ 3 потенциальных змеиных пути и проверил, какой из них имеет максимальную сумму и делится на 3.
Проблема в том, что этот подход не настолько эффективен, он займет O (n ^ 4), что довольно медленно, и эта проблема, похоже, может быть решена с помощью динамического программирования.
Любая помощь будет с благодарностью
Комментарии:
1. Можете ли вы уточнить определение змеиного пути? 1. Каждая строка должна быть посещена
exactly
один раз илиAt least
один раз? 2. Существует ли какое-либо ограничение (at least
/exactly
один раз) для посещения столбцов? 3. Должен ли этот путь начинаться и заканчиваться в каких-либо определенных местах? (например, начинается в верхнем левом углу и заканчивается в нижнем правом углу)?2. Поскольку, исходя из буквального понимания вашего определения: путь может начинаться где угодно , заканчиваться где угодно , может посещать каждую строку любое количество раз (но хотя бы один раз ), может посещать каждый столбец любое количество раз (даже ноль ), количество возможных змеиных путей будет намного выше
n^3
. Вот почему я думаю, что правильное определение может быть более ограниченным, чем это.3. @Alireza Я имел в виду, что в каждой строке должен быть один элемент (не более или менее одного), а в двух соседних строках элементы должны находиться в соседних столбцах, в основном это означает, что я могу перемещаться только по диагонали в матрице
4. Еще несколько открытых вопросов: может ли путь начинаться с любого столбца? Может ли он заканчиваться на любом столбце? Или существуют фиксированные цели (
top-left
иbottom-right
или некоторые такие)?5. Лучшим был бы пример. Можете ли вы показать небольшую сетку, которая демонстрирует вопрос и правильный ответ?
Ответ №1:
Я нашел это интересной проблемой для решения. Вот потенциальное решение, которое найдет путь, подобный следующему:
Start at index 5 ---v
__
7 6 5 4 3 | 2| 1 (start) 2
'-- __
2 4 6 8 10 12 |14| (right) 14
__ /--'
2 7 1 8 2 | 8| 1 (left) 8
__ /--'
3 1 4 1 | 5| 9 2 (left) 5
'-- __
2 3 5 7 11 |13| 17 (right) 13
'-- __
8 6 7 5 3 0 | 9| (right) 9
'--' ----
total: 51
который будет отображаться следующим образом:
{path: "RLLRR", startIndex: 5, total: 51}
Реализация выглядит следующим образом:
const maxMod3Path = (grid) =>
[...grid] .reverse () .reduce ((prev, row, ri) => row .map ((c, i) =>
[
...(prev [i - 1] || []).map(({n, p}) => ri == 0 ? {n, p} : ({n, p: 'L' p})),
...(prev [i 1] || []).map(({n, p}) => ri == 0 ? {n, p} : ({n, p: 'R' p})),
]
.map (({n, p}) => ({n: n c, p}))
.reduce (
(a, {n, p}) => {a [n % 3] = (n > a [n % 3] .n ? {n, p} : a [n % 3]); return a},
[{n: 0}, {n: 0}, {n: 0}]
)
),
grid [0] .map ((c) => [{n: 0, p: ''}, {n: 0, p: ''}, {n: 0, p: ''}])
)
.map (([{n, p}], startIndex) => ({total: n, path: p, startIndex}))
.reduce ((a, x) => x.total > a.total ? x : a, {total: 0})
const grid = [
[ 7, 6, 5, 4, 3, 2, 1 ],
[ 2, 4, 6, 8, 10, 12, 14 ],
[ 2, 7, 1, 8, 2, 8, 1 ],
[ 3, 1, 4, 1, 5, 9, 2 ],
[ 2, 3, 5, 7, 11, 13, 17 ],
[ 8, 6, 7, 5, 3, 0, 9 ],
]
console .log (maxMod3Path (grid))
Сейчас у меня нет времени писать длинное объяснение этого кода, но краткий обзор заключается в том, что это использует динамическое программирование, следуя алгоритму, описанному Эхсаном Герайли. Мы начинаем с массива шириной каждой строки, каждая запись которого содержит массив из трех копий {n: 0, p: ''}
, по одной для 0
, 1
, и 2
результатов модуля для 3
.
Начиная с нижней строки, мы вычисляем для каждой ячейки результаты, найденные путем перемещения вниз и влево или вправо от ячейки, сохраняя общее количество и путь в правильном по модулю сегменте, если общее количество больше текущего значения для сегмента.
В конце у нас есть максимум для каждого сохраненного сегмента. Теперь мы можем удалить сегменты 1
и 2
, переименовав переменные и включив индекс для начала. ( .map (([{n, p}], startIndex) => ({total: n, path: p, startIndex}))
)
Это даст нам что-то вроде этого:
[
{path: "RLRRR", startIndex: 0, total: 24},
{path: "RRRLL", startIndex: 1, total: 39},
{path: "LRRRL", startIndex: 2, total: 27},
{path: "LRRRR", startIndex: 3, total: 45},
{path: "RLRLL", startIndex: 4, total: 42},
{path: "RLLRR", startIndex: 5, total: 51},
{path: "LRLLL", startIndex: 6, total: 39}
]
Затем reduce
вызов в последней строке выбирает тот, у которого наибольшее общее количество.
Я думаю, что у меня правильные требования. Но если путь также может опускаться прямо вниз, вы также можете добавить это в очевидном месте:
...(prev [ i ] || []).map(({n, p}) => ri == 0 ? {n, p} : ({n, p: 'D' p})),
что теперь даст результат
{path: "RRDRD", startIndex: 3, total: 57}
Наконец, если ваша сетка может содержать отрицательные числа, вам следует заменить все {n: 0}
экземпляры на {n: -Infinity}
. (Это не было тщательно протестировано, но похоже, что это сработает.)
Я хотел бы услышать, соответствует ли это требованиям.
Ответ №2:
Прежде всего, утверждение I calculated that there are n^3 potential snake paths
неверно, ситуаций намного больше, и на самом деле это O(2^n)
Вы можете использовать следующий динамический подход, который O(n^2)
- В начале просто сохраните последнюю строку массива и на каждом шаге добавляйте строки снизу одну за другой
- На каждом шаге предположим, что вы сохраняете эти 3 значения для каждой ячейки Current_Array
- Максимальный путь с СУММОЙ % 3 == 0, максимальный путь с СУММОЙ % 3 == 1 и … == 2
- Для новой добавленной строки вы можете легко вычислить эти 3 параметра из нижней строки, например, для index (i, j) просто проверьте 3 параметра (i 1,j 1) и (i 1,j-1) и в соответствии со значением index(i, j)%3 вычисляет его параметры
- В конце найдите максимальное значение параметра (SUM % 3 ==0) в массиве с O (n ^ 2)
Комментарии:
1. Я просто играл с этим и придумал то же самое. Границы имеют некоторую сложность, но, похоже, это сработает, пока нет никаких дополнительных ограничений в отношении начальных / конечных столбцов.