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

Число Фибоначчи



Есть несколько вариантов решения этой проблемы. Мы знакомы с первой рекурсией: мы рекурсивно вычисляем fib(n-1) и fib(n-2), пока не встретимся с граничным случаем. Однако рекурсивное вычисление числа Фибоначчи приведет к O(2^N) времени выполнения. Применяя динамическое программирование, мы можем уменьшить его до O(N), создав массив для запоминания предыдущего результата.

Например, если задано N 8. Мы создадим массив для хранения числа Фибоначчи в range[2, N].

original dp: [0, 1]
when i = 2, fib(2) = dp[2–1] + dp[2–2], dp = [0,1,1];
when i = 3, fib(3) = dp[3–1] + dp[3–2], dp = [0,1,1,2];
when i = 4, fib(4) = dp[4–1] + dp[4–2], dp = [0,1,1,2,3];
when i = 5, fib(5) = dp[5–1] + dp[5–2], dp = [0,1,1,2,3,5];
when i = 6, fib(6) = dp[6–1] + dp[6–2], dp = [0,1,1,2,3,5,8];
when i = 7, fib(7) = dp[7–1] + dp[7–2], dp = [[0,1,1,2,3,5,8,13];
when i = 8, fib(8) = dp[8–1] + dp[8–2], dp = [[0,1,1,2,3,5,8,13,21];
fib(8) = 21

Решение на JS:

var fib = function(N) {
    if (N === 0) {
        return 0
    } else if (N == 1) {
        return 1
    }
    var dp = [0, 1]
    for (var i = 2; i <= N; i++) {
        var number = dp[i - 1] + dp[i - 2]
        dp.push(number)
    }
    return dp[dp.length - 1]
};

Самая длинная возрастающая подпоследовательность



Алгоритм динамического программирования часто используется для поиска максимальных или минимальных свойств данного массива. Для этой задачи у нас может быть dp array для запоминания с помощью dp [i], самой длинной возрастающей длины последовательности.

array = [10,9,2,5,3,7,101,18]
dp = [1,1,1,2,2,3,4,4]
longest increasing subsequence = 4

Код JS:

var lengthOfLIS = function(nums) {
    if (!nums || nums.length === 0) {
        return 0
    }
    
    if (nums.length === 1) {
        return 1
    }
    
    var dp = new Array(nums.length).fill(0)
    var max = 0
    
    for (var i = 0; i < nums.length; i++) {
        dp[i] = 1
        for (var j = 0; j < i; j++) {
            if (nums[i] > nums[j]) {
                dp[i] = dp[i] > dp[j] + 1 ? dp[i] : dp[j] + 1
            }
        }
        max = Math.max(max, dp[i])
    }
    console.log(dp)
    return max
};

Минимальная сумма пути



Подобно массиву, матрица также может хранить предыдущие результаты. Например, мы можем matrix[i][j] хранить минимальную сумму пути в текущей позиции.

matrix = [
          [1,3,1],
          [1,5,1],
          [4,2,1]]
    dp = [
          [1,4,5],
          [2,7,6],
          [6,8,7]]    minimum path = 7

Код JS:

var minPathSum = function(grid) {
    if (!grid || grid.length === 0 || grid[0].length === 0) {
        return 0;
    }
    var m = grid.length
    var n = grid[0].length
    for (var i = 0; i < grid.length; i++) {
        for (var j = 0; j < grid[i].length; j++) {
            if (i === 0 && j === 0) {
                grid[i][j] *= 1
            } else if (i === 0 && j !== 0) {
                grid[i][j] += grid[i][j - 1]
            } else if(i !== 0 && j === 0) {
                grid[i][j] += grid[i - 1][j]
            } else {
                grid[i][j] += Math.min(grid[i][j - 1], grid[i - 1][j])
            }
        }
    }
    return grid[m - 1][n - 1]
};