Subsecuencia creciente más larga | DP-3 – Part 2

Ya hemos discutido los subproblemas superpuestos y las propiedades de la subestructura óptima
Ahora, analicemos el problema de la subsecuencia creciente más larga (LIS) como un problema de ejemplo que se puede resolver mediante la programación dinámica. 

El problema de la subsecuencia creciente más larga (LIS) es encontrar la longitud de la subsecuencia más larga de una secuencia dada de modo que todos los elementos de la subsecuencia se clasifiquen en orden creciente. Por ejemplo, la longitud de LIS para {10, 22, 9, 33, 21, 50, 41, 60, 80} es 6 y LIS es {10, 22, 33, 50, 60, 80}. 

longest-increasing-subsequence

Ejemplos: 

Input: arr[] = {3, 10, 2, 1, 20}
Output: Length of LIS = 3
The longest increasing subsequence is 3, 10, 20

Input: arr[] = {3, 2}
Output: Length of LIS = 1
The longest increasing subsequences are {3} and {2}

Input: arr[] = {50, 3, 10, 7, 40, 80}
Output: Length of LIS = 4
The longest increasing subsequence is {3, 7, 40, 80}
 

Método 1 : Recursión.
Subestructura óptima: Sea arr[0..n-1] la array de entrada y L(i) la longitud del LIS que termina en el índice i, de modo que arr[i] es el último elemento del LIS.

Entonces, L(i) puede escribirse recursivamente como: 

L(i) = 1 + max( L(j) ) where 0 < j < i and arr[j] < arr[i]; or
L(i) = 1, if no such j exists.

Para encontrar el LIS para una array dada, necesitamos devolver max(L(i)) donde 0 < i < n.
Formalmente, la longitud de la subsecuencia creciente más larga que termina en el índice i será 1 mayor que la longitud máxima de todas las subsecuencias crecientes más largas que terminan en los índices anteriores a i, donde arr[j] < arr[i] (j < i).
Por lo tanto, vemos que el problema LIS satisface la propiedad de subestructura óptima ya que el problema principal se puede resolver utilizando soluciones a los subproblemas.

El árbol recursivo que se muestra a continuación hará que el enfoque sea más claro:  

Input  : arr[] = {3, 10, 2, 11}
f(i): Denotes LIS of subarray ending at index 'i'

(LIS(1)=1)

      f(4)  {f(4) = 1 + max(f(1), f(2), f(3))}
  /    |    \
f(1)  f(2)  f(3) {f(3) = 1, f(2) and f(1) are > f(3)}
       |      |  \
      f(1)  f(2)  f(1) {f(2) = 1 + max(f(1)}
              |
            f(1) {f(1) = 1}

A continuación se muestra la implementación del enfoque recursivo: 

C++

/* A Naive C++ recursive implementation
of LIS problem */
#include <iostream>
using namespace std;
 
/* To make use of recursive calls, this
function must return two things:
1) Length of LIS ending with element arr[n-1].
    We use max_ending_here for this purpose
2) Overall maximum as the LIS may end with
    an element before arr[n-1] max_ref is
    used this purpose.
The value of LIS of full array of size n
is stored in *max_ref which is our final result
*/
int _lis(int arr[], int n, int* max_ref)
{
    /* Base case */
    if (n == 1)
        return 1;
 
    // 'max_ending_here' is length of LIS
    // ending with arr[n-1]
    int res, max_ending_here = 1;
 
    /* Recursively get all LIS ending with arr[0],
    arr[1] ... arr[n-2]. If arr[i-1] is smaller
    than arr[n-1], and max ending with arr[n-1]
    needs to be updated, then update it */
    for (int i = 1; i < n; i++) {
        res = _lis(arr, i, max_ref);
        if (arr[i - 1] < arr[n - 1]
            && res + 1 > max_ending_here)
            max_ending_here = res + 1;
    }
 
    // Compare max_ending_here with the overall
    // max. And update the overall max if needed
    if (*max_ref < max_ending_here)
        *max_ref = max_ending_here;
 
    // Return length of LIS ending with arr[n-1]
    return max_ending_here;
}
 
// The wrapper function for _lis()
int lis(int arr[], int n)
{
    // The max variable holds the result
    int max = 1;
 
    // The function _lis() stores its result in max
    _lis(arr, n, &max);
 
    // returns max
    return max;
}
 
/* Driver program to test above function */
int main()
{
    int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };
    int n = sizeof(arr) / sizeof(arr[0]);
    cout <<"Length of lis is "<< lis(arr, n);
    return 0;
}
// This code is contributed by shivanisinghss2110

C

/* A Naive C recursive implementation
of LIS problem */
#include <stdio.h>
#include <stdlib.h>
 
/* To make use of recursive calls, this
function must return two things:
1) Length of LIS ending with element arr[n-1].
    We use max_ending_here for this purpose
2) Overall maximum as the LIS may end with
    an element before arr[n-1] max_ref is
    used this purpose.
The value of LIS of full array of size n
is stored in *max_ref which is our final result
*/
int _lis(int arr[], int n, int* max_ref)
{
    /* Base case */
    if (n == 1)
        return 1;
 
    // 'max_ending_here' is length of LIS
    // ending with arr[n-1]
    int res, max_ending_here = 1;
 
    /* Recursively get all LIS ending with arr[0],
    arr[1] ... arr[n-2]. If arr[i-1] is smaller
    than arr[n-1], and max ending with arr[n-1]
    needs to be updated, then update it */
    for (int i = 1; i < n; i++) {
        res = _lis(arr, i, max_ref);
        if (arr[i - 1] < arr[n - 1]
            && res + 1 > max_ending_here)
            max_ending_here = res + 1;
    }
 
    // Compare max_ending_here with the overall
    // max. And update the overall max if needed
    if (*max_ref < max_ending_here)
        *max_ref = max_ending_here;
 
    // Return length of LIS ending with arr[n-1]
    return max_ending_here;
}
 
// The wrapper function for _lis()
int lis(int arr[], int n)
{
    // The max variable holds the result
    int max = 1;
 
    // The function _lis() stores its result in max
    _lis(arr, n, &max);
 
    // returns max
    return max;
}
 
/* Driver program to test above function */
int main()
{
    int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };
    int n = sizeof(arr) / sizeof(arr[0]);
    printf("Length of lis is %d", lis(arr, n));
    return 0;
}

Java

/* A Naive Java Program for LIS Implementation */
class LIS {
    static int max_ref; // stores the LIS
 
    /* To make use of recursive calls, this function must
    return two things: 1) Length of LIS ending with element
    arr[n-1]. We use max_ending_here for this purpose 2)
    Overall maximum as the LIS may end with an element
       before arr[n-1] max_ref is used this purpose.
    The value of LIS of full array of size n is stored in
    *max_ref which is our final result */
    static int _lis(int arr[], int n)
    {
        // base case
        if (n == 1)
            return 1;
 
        // 'max_ending_here' is length of LIS ending with
        // arr[n-1]
        int res, max_ending_here = 1;
 
        /* Recursively get all LIS ending with arr[0],
           arr[1] ... arr[n-2]. If   arr[i-1] is smaller
           than arr[n-1], and max ending with arr[n-1] needs
           to be updated, then update it */
        for (int i = 1; i < n; i++) {
            res = _lis(arr, i);
            if (arr[i - 1] < arr[n - 1]
                && res + 1 > max_ending_here)
                max_ending_here = res + 1;
        }
 
        // Compare max_ending_here with the overall max. And
        // update the overall max if needed
        if (max_ref < max_ending_here)
            max_ref = max_ending_here;
 
        // Return length of LIS ending with arr[n-1]
        return max_ending_here;
    }
 
    // The wrapper function for _lis()
    static int lis(int arr[], int n)
    {
        // The max variable holds the result
        max_ref = 1;
 
        // The function _lis() stores its result in max
        _lis(arr, n);
 
        // returns max
        return max_ref;
    }
 
    // driver program to test above functions
    public static void main(String args[])
    {
        int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };
        int n = arr.length;
        System.out.println("Length of lis is " + lis(arr, n)
                           + "\n");
    }
}
/*This code is contributed by Rajat Mishra*/

Python3

# A naive Python implementation of LIS problem
 
""" To make use of recursive calls, this function must return
 two things:
 1) Length of LIS ending with element arr[n-1]. We use
 max_ending_here for this purpose
 2) Overall maximum as the LIS may end with an element
 before arr[n-1] max_ref is used this purpose.
 The value of LIS of full array of size n is stored in
 *max_ref which is our final result """
 
# global variable to store the maximum
global maximum
 
 
def _lis(arr, n):
 
    # to allow the access of global variable
    global maximum
 
    # Base Case
    if n == 1:
        return 1
 
    # maxEndingHere is the length of LIS ending with arr[n-1]
    maxEndingHere = 1
 
    """Recursively get all LIS ending with arr[0], arr[1]..arr[n-2]
       IF arr[n-1] is smaller than arr[n-1], and max ending with
       arr[n-1] needs to be updated, then update it"""
    for i in range(1, n):
        res = _lis(arr, i)
        if arr[i-1] < arr[n-1] and res+1 > maxEndingHere:
            maxEndingHere = res + 1
 
    # Compare maxEndingHere with overall maximum. And
    # update the overall maximum if needed
    maximum = max(maximum, maxEndingHere)
 
    return maxEndingHere
 
 
def lis(arr):
 
    # to allow the access of global variable
    global maximum
 
    # length of arr
    n = len(arr)
 
    # maximum variable holds the result
    maximum = 1
 
    # The function _lis() stores its result in maximum
    _lis(arr, n)
 
    return maximum
 
 
# Driver program to test the above function
arr = [10, 22, 9, 33, 21, 50, 41, 60]
n = len(arr)
print ("Length of lis is ", lis(arr))
 
# This code is contributed by NIKHIL KUMAR SINGH

C#

using System;
 
/* A Naive C# Program for LIS Implementation */
class LIS {
    static int max_ref; // stores the LIS
 
    /* To make use of recursive calls, this function must
    return two things: 1) Length of LIS ending with element
    arr[n-1]. We use max_ending_here for this purpose 2)
    Overall maximum as the LIS may end with an element
       before arr[n-1] max_ref is used this purpose.
    The value of LIS of full array of size n is stored in
    *max_ref which is our final result */
    static int _lis(int[] arr, int n)
    {
        // base case
        if (n == 1)
            return 1;
 
        // 'max_ending_here' is length of LIS ending with
        // arr[n-1]
        int res, max_ending_here = 1;
 
        /* Recursively get all LIS ending with arr[0],
           arr[1] ... arr[n-2]. If   arr[i-1] is smaller
           than arr[n-1], and max ending with arr[n-1] needs
           to be updated, then update it */
        for (int i = 1; i < n; i++) {
            res = _lis(arr, i);
            if (arr[i - 1] < arr[n - 1]
                && res + 1 > max_ending_here)
                max_ending_here = res + 1;
        }
 
        // Compare max_ending_here with the overall max. And
        // update the overall max if needed
        if (max_ref < max_ending_here)
            max_ref = max_ending_here;
 
        // Return length of LIS ending with arr[n-1]
        return max_ending_here;
    }
 
    // The wrapper function for _lis()
    static int lis(int[] arr, int n)
    {
        // The max variable holds the result
        max_ref = 1;
 
        // The function _lis() stores its result in max
        _lis(arr, n);
 
        // returns max
        return max_ref;
    }
 
    // driver program to test above functions
    public static void Main()
    {
        int[] arr = { 10, 22, 9, 33, 21, 50, 41, 60 };
        int n = arr.Length;
        Console.Write("Length of lis is " + lis(arr, n)
                      + "\n");
    }
}

Javascript

<script>
 
/* A Naive javascript Program for LIS Implementation */
 
 
    let  max_ref; // stores the LIS
    /* To make use of recursive calls, this function must return
   two things:
   1) Length of LIS ending with element arr[n-1]. We use
      max_ending_here for this purpose
   2) Overall maximum as the LIS may end with an element
      before arr[n-1] max_ref is used this purpose.
   The value of LIS of full array of size n is stored in
   *max_ref which is our final result */
    function  _lis(arr,n)
    {
        // base case
        if (n == 1)
            return 1;
         
        // 'max_ending_here' is length of LIS ending with arr[n-1]
        let res, max_ending_here = 1;
        /* Recursively get all LIS ending with arr[0], arr[1] ...
           arr[n-2]. If   arr[i-1] is smaller than arr[n-1], and
           max ending with arr[n-1] needs to be updated, then
           update it */
        for (let i = 1; i < n; i++)
        {
            res = _lis(arr, i);
            if (arr[i-1] < arr[n-1] && res + 1 > max_ending_here)
                max_ending_here = res + 1;
        }
        // Compare max_ending_here with the overall max. And
        // update the overall max if needed
        if (max_ref < max_ending_here)
            max_ref = max_ending_here;
         
        // Return length of LIS ending with arr[n-1]
        return max_ending_here;
    }
     
    // The wrapper function for _lis()
    function  lis(arr,n)
    {
        // The max variable holds the result
        max_ref = 1;
         
        // The function _lis() stores its result in max
        _lis( arr, n);
         
        // returns max
        return max_ref;
    }
     
    // driver program to test above functions
    let arr=[10, 22, 9, 33, 21, 50, 41, 60 ]
    let n = arr.length;
    document.write("Length of lis is "
                           + lis(arr, n) + "<br>");
     
    // This code is contributed by avanitrachhadiya2155
     
</script>
Producción

Length of lis is 5

Análisis de Complejidad: 

  • Complejidad de tiempo: La complejidad de tiempo de este enfoque recursivo es exponencial ya que hay un caso de subproblemas superpuestos como se explica en el diagrama de árbol recursivo anterior.
  • Espacio Auxiliar: O(1). No se utiliza espacio externo para almacenar valores aparte del espacio de pila interno.

Método 2 : Programación Dinámica.
Podemos ver que hay muchos subproblemas en la solución recursiva anterior que se resuelven una y otra vez. Por lo tanto, este problema tiene la propiedad de subestructura superpuesta y el recálculo de los mismos subproblemas se puede evitar mediante Memoización o Tabulación.

La simulación de enfoque aclarará las cosas:   

Input  : arr[] = {3, 10, 2, 11}
LIS[] = {1, 1, 1, 1} (initially)

Simulación iterativa: 

  1. array[2] > array[1] {LIS[2] = max(LIS [2], LIS[1]+1)=2}
  2. arr[3] < arr[1] {Sin cambios}
  3. arr[3] < arr[2] {Sin cambios}
  4. array[4] > array[1] {LIS[4] = max(LIS[4], LIS[1]+1)=2}
  5. array[4] > array[2] {LIS[4] = max(LIS[4], LIS[2]+1)=3}
  6. array[4] > array[3] {LIS[4] = max(LIS [4], LIS[3]+1)=3}

Podemos evitar el recálculo de los subproblemas usando la tabulación como se muestra en el siguiente código: 

A continuación se muestra la implementación del enfoque anterior:  

C++

/* Dynamic Programming C++ implementation
   of LIS problem */
#include <bits/stdc++.h>
using namespace std;
 
/* lis() returns the length of the longest
  increasing subsequence in arr[] of size n */
int lis(int arr[], int n)
{
    int lis[n];
 
    lis[0] = 1;
 
    /* Compute optimized LIS values in
       bottom up manner */
    for (int i = 1; i < n; i++) {
        lis[i] = 1;
        for (int j = 0; j < i; j++)
            if (arr[i] > arr[j] && lis[i] < lis[j] + 1)
                lis[i] = lis[j] + 1;
    }
 
    // Return maximum value in lis[]
    return *max_element(lis, lis + n);
}
 
/* Driver program to test above function */
int main()
{
    int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };
    int n = sizeof(arr) / sizeof(arr[0]);
    printf("Length of lis is %d\n", lis(arr, n));
 
    return 0;
}

Java

/* Dynamic Programming Java implementation
   of LIS problem */
 
class LIS {
    /* lis() returns the length of the longest
       increasing subsequence in arr[] of size n */
    static int lis(int arr[], int n)
    {
        int lis[] = new int[n];
        int i, j, max = 0;
 
        /* Initialize LIS values for all indexes */
        for (i = 0; i < n; i++)
            lis[i] = 1;
 
        /* Compute optimized LIS values in
           bottom up manner */
        for (i = 1; i < n; i++)
            for (j = 0; j < i; j++)
                if (arr[i] > arr[j] && lis[i] < lis[j] + 1)
                    lis[i] = lis[j] + 1;
 
        /* Pick maximum of all LIS values */
        for (i = 0; i < n; i++)
            if (max < lis[i])
                max = lis[i];
 
        return max;
    }
 
    public static void main(String args[])
    {
        int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };
        int n = arr.length;
        System.out.println("Length of lis is " + lis(arr, n)
                           + "\n");
    }
}
/*This code is contributed by Rajat Mishra*/

Python3

# Dynamic programming Python implementation
# of LIS problem
 
# lis returns length of the longest
# increasing subsequence in arr of size n
 
 
def lis(arr):
    n = len(arr)
 
    # Declare the list (array) for LIS and
    # initialize LIS values for all indexes
    lis = [1]*n
 
    # Compute optimized LIS values in bottom up manner
    for i in range(1, n):
        for j in range(0, i):
            if arr[i] > arr[j] and lis[i] < lis[j] + 1:
                lis[i] = lis[j]+1
 
    # Initialize maximum to 0 to get
    # the maximum of all LIS
    maximum = 0
 
    # Pick maximum of all LIS values
    for i in range(n):
        maximum = max(maximum, lis[i])
 
    return maximum
# end of lis function
 
 
# Driver program to test above function
arr = [10, 22, 9, 33, 21, 50, 41, 60]
print ("Length of lis is", lis(arr))
# This code is contributed by Nikhil Kumar Singh

C#

/* Dynamic Programming C# implementation of LIS problem */
 
using System;
class LIS {
    /* lis() returns the length of the longest increasing
    subsequence in arr[] of size n */
    static int lis(int[] arr, int n)
    {
        int[] lis = new int[n];
        int i, j, max = 0;
 
        /* Initialize LIS values for all indexes */
        for (i = 0; i < n; i++)
            lis[i] = 1;
 
        /* Compute optimized LIS values in bottom up manner
         */
        for (i = 1; i < n; i++)
            for (j = 0; j < i; j++)
                if (arr[i] > arr[j] && lis[i] < lis[j] + 1)
                    lis[i] = lis[j] + 1;
 
        /* Pick maximum of all LIS values */
        for (i = 0; i < n; i++)
            if (max < lis[i])
                max = lis[i];
 
        return max;
    }
 
    public static void Main()
    {
        int[] arr = { 10, 22, 9, 33, 21, 50, 41, 60 };
        int n = arr.Length;
        Console.WriteLine("Length of lis is " + lis(arr, n)
                          + "\n");
    }
 
    // This code is contributed by Ryuga
}

Javascript

<script>
 
// Dynamic Programming Javascript implementation
// of LIS problem
  
// lis() returns the length of the longest
// increasing subsequence in arr[] of size n
function lis(arr, n)
{
    let lis = Array(n).fill(0);
    let i, j, max = 0;
 
    // Initialize LIS values for all indexes
    for(i = 0; i < n; i++)
        lis[i] = 1;
 
    // Compute optimized LIS values in
    // bottom up manner
    for(i = 1; i < n; i++)
        for(j = 0; j < i; j++)
            if (arr[i] > arr[j] && lis[i] < lis[j] + 1)
                lis[i] = lis[j] + 1;
 
    // Pick maximum of all LIS values
    for(i = 0; i < n; i++)
        if (max < lis[i])
            max = lis[i];
 
    return max;
}
 
// Driver code
let arr = [ 10, 22, 9, 33, 21, 50, 41, 60 ];
let n = arr.length;
document.write("Length of lis is " + lis(arr, n) + "\n");
 
// This code is contributed by avijitmondal1998
 
</script>
Producción

Length of lis is 5

Análisis de Complejidad: 

  • Complejidad Temporal: O(n 2 ). 
    Como se usa el bucle anidado.
  • Espacio Auxiliar: O(n). 
    Uso de cualquier array para almacenar valores LIS en cada índice.

Nota: La complejidad de tiempo de la solución de programación dinámica (DP) anterior es O (n ^ 2) y hay una solución O (N log N) para el problema LIS. No hemos discutido la solución O (N log N) aquí ya que el propósito de esta publicación es explicar la programación dinámica con un ejemplo simple. Consulte la publicación a continuación para obtener la solución O (N log N). 
Tamaño de subsecuencia creciente más largo (N log N)

Método 3: Programación Dinámica

Si observamos de cerca el problema, podemos convertirlo en el Problema de subsecuencia común más largo. En primer lugar, crearemos otra array de elementos únicos de la array original y la ordenaremos. Ahora, la subsecuencia creciente más larga de nuestra array debe estar presente como una subsecuencia en nuestra array ordenada. Es por eso que nuestro problema ahora se reduce a encontrar la subsecuencia común entre las dos arrays.

Eg. arr =[50,3,10,7,40,80]
    // Sorted array
    arr1 = [3,7,10,40,50,80]
    // LIS is longest common subsequence between the two arrays
    ans = 4
    The longest increasing subsequence is {3, 7, 40, 80}

C++

// Dynamic Programming Approach of Finding LIS by reducing
// the problem to longest common Subsequence
#include <bits/stdc++.h>
using namespace std;
 
/* lis() returns the length of the longest
  increasing subsequence in arr[] of size n */
int lis(int arr[], int n)
{
    vector<int> b;
    set<int> s;
   
    // setting iterator for set
    set<int>::iterator it;
   
    // storing unique elements
    for (int i = 0; i < n; i++) {
        s.insert(arr[i]);
    }
   
    // creating sorted vector
    for (it = s.begin(); it != s.end(); it++) {
        b.push_back(*it);
    }
    int m = b.size(), i, j;
    int dp[m + 1][n + 1];
   
    // storing -1 in dp multidimensional array
    for (i = 0; i < m + 1; i++) {
        for (j = 0; j < n + 1; j++) {
            dp[i][j] = -1;
        }
    }
    // Finding Longest common Subsequence of the two
    // arrays
    for (i = 0; i < m + 1; i++) {
        for (j = 0; j < n + 1; j++) {
            if (i == 0 || j == 0) {
                dp[i][j] = 0;
            }
            else if (arr[j - 1] == b[i - 1]) {
                dp[i][j] = 1 + dp[i - 1][j - 1];
            }
            else {
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
    return dp[m][n];
}
 
/* Driver program to test above function */
int main()
{
    int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };
    int n = sizeof(arr) / sizeof(arr[0]);
    printf("Length of lis is %d\n", lis(arr, n));
}
 
/* This code is contributed by Arun Bang */

Java

import static java.lang.Math.max;
 
import java.util.SortedSet;
import java.util.TreeSet;
 
// Dynamic Programming Approach of Finding LIS by reducing
// the problem to longest common Subsequence
public class Main {
 
    /* lis() returns the length of the longest
    increasing subsequence in arr[] of size n */
    static int lis(int arr[], int n)
    {
        SortedSet<Integer> hs = new TreeSet<Integer>();
        // Storing and Sorting unique elements.
        for (int i = 0; i < n; i++)
            hs.add(arr[i]);
        int lis[] = new int[hs.size()];
        int k = 0;
        // Storing all the unique values in a sorted manner.
        for (int val : hs) {
            lis[k] = val;
            k++;
        }
        int m = k, i, j;
        int dp[][] = new int[m + 1][n + 1];
 
        // Storing -1 in dp multidimensional array.
        for (i = 0; i < m + 1; i++) {
            for (j = 0; j < n + 1; j++) {
                dp[i][j] = -1;
            }
        }
 
        // Finding the Longest Common Subsequence of the two
        // arrays
        for (i = 0; i < m + 1; i++) {
            for (j = 0; j < n + 1; j++) {
                if (i == 0 || j == 0) {
                    dp[i][j] = 0;
                }
                else if (arr[j - 1] == lis[i - 1]) {
                    dp[i][j] = 1 + dp[i - 1][j - 1];
                }
                else {
                    dp[i][j]
                        = max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[m][n];
    }
 
    // Driver Program for the above test function.
    public static void main(String[] args)
    {
        int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };
        int n = arr.length;
        System.out.println("Length of lis is " + lis(arr, n)
                           + "\n");
    }
}
 
// This Code is Contributed by Omkar Subhash Ghongade

Python3

# Dynamic Programming Approach of Finding LIS by reducing the problem to longest common Subsequence
 
 
def lis(a):
    n = len(a)
    # Creating the sorted list
    b = sorted(list(set(a)))
    m = len(b)
 
    # Creating dp table for storing the answers of sub problems
    dp = [[-1 for i in range(m+1)] for j in range(n+1)]
 
    # Finding Longest common Subsequence of the two arrays
    for i in range(n+1):
 
        for j in range(m+1):
            if i == 0 or j == 0:
                dp[i][j] = 0
            elif a[i-1] == b[j-1]:
                dp[i][j] = 1+dp[i-1][j-1]
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    return dp[-1][-1]
 
 
# Driver program to test above function
arr = [10, 22, 9, 33, 21, 50, 41, 60]
print("Length of lis is ", lis(arr))
# This code is Contributed by Dheeraj Khatri
Producción

Length of lis is 5

Análisis de Complejidad : O(n*n)

Como se usa el bucle anidado

Complejidad espacial : O(n*n)

Como array se utiliza para almacenar los valores.

Método 4: Memoización DP

Esta es una extensión del método recursivo.

Podemos ver que hay muchos subproblemas en la solución recursiva anterior que se resuelven una y otra vez. Por lo tanto, este problema tiene la propiedad de subestructura superpuesta y el recálculo de los mismos subproblemas se puede evitar utilizando Memoization

C++

/* A Naive C++ recursive implementation
of LIS problem */
#include <bits/stdc++.h>
#include <iostream>
using namespace std;
 
/* To make use of recursive calls, this
function must return two things:
1) Length of LIS ending with element arr[n-1].
    We use max_ending_here for this purpose
2) Overall maximum as the LIS may end with
    an element before arr[n-1] max_ref is
    used this purpose.
The value of LIS of full array of size n
is stored in *max_ref which is our final result
*/
 
int f(int idx, int prev_idx, int n, int a[],
      vector<vector<int> >& dp)
{
    if (idx == n) {
        return 0;
    }
 
    if (dp[idx][prev_idx + 1] != -1) {
        return dp[idx][prev_idx + 1];
    }
 
    int notTake = 0 + f(idx + 1, prev_idx, n, a, dp);
    int take = INT_MIN;
    if (prev_idx == -1 || a[idx] > a[prev_idx]) {
        take = 1 + f(idx + 1, idx, n, a, dp);
    }
 
    return dp[idx][prev_idx + 1] = max(take, notTake);
}
// Function to find length of longest increasing
// subsequence.
int longestSubsequence(int n, int a[])
{
    vector<vector<int> > dp(n + 1, vector<int>(n + 1, -1));
    return f(0, -1, n, a, dp);
}
 
/* Driver program to test above function */
int main()
{
    int a[] = { 3, 10, 2, 1, 20 };
    int n = sizeof(a) / sizeof(a[0]);
    cout << "Length of lis is " << longestSubsequence(n, a);
    return 0;
}

Java

/* A Memoization Java Program for LIS Implementation */
import java.util.Arrays;
import java.lang.*;
class LIS {
 
  /* To make use of recursive calls, this function must
    return two things: 1) Length of LIS ending with element
    arr[n-1]. We use max_ending_here for this purpose 2)
    Overall maximum as the LIS may end with an element
       before arr[n-1] max_ref is used this purpose.
    The value of LIS of full array of size n is stored in
    *max_ref which is our final result */
  static int f(int idx, int prev_idx, int n, int a[],
               int[][] dp)
  {
    if (idx == n) {
      return 0;
    }
 
    if (dp[idx][prev_idx + 1] != -1) {
      return dp[idx][prev_idx + 1];
    }
 
    int notTake = 0 + f(idx + 1, prev_idx, n, a, dp);
    int take = Integer.MIN_VALUE;
    if (prev_idx == -1 || a[idx] > a[prev_idx]) {
      take = 1 + f(idx + 1, idx, n, a, dp);
    }
 
    return dp[idx][prev_idx + 1] = Math.max(take, notTake);
  }
 
  // The wrapper function for _lis()
  static int lis(int arr[], int n)
  {
 
    // The function _lis() stores its result in max
    int dp[][] = new int[n+1][n+1];
    for (int row[] : dp)
      Arrays.fill(row, -1);
 
    return f(0, -1, n, arr, dp);
 
 
  }
 
  // driver program to test above functions
  public static void main(String args[])
  {
    int a[] = { 3, 10, 2, 1, 20 };
    int n = a.length;
    System.out.println("Length of lis is " + lis(a, n)
                       + "\n");
  }
}
 
// This code is contributed by Sanskar.

Python3

# A Naive Python recursive implementation
# of LIS problem
 
# To make use of recursive calls, this
# function must return two things:
# 1) Length of LIS ending with element arr[n-1].
#     We use max_ending_here for this purpose
# 2) Overall maximum as the LIS may end with
#     an element before arr[n-1] max_ref is
#     used this purpose.
# The value of LIS of full array of size n
# is stored in *max_ref which is our final result
import sys
 
def f(idx, prev_idx, n, a,dp):
 
    if (idx == n):
        return 0
 
    if (dp[idx][prev_idx + 1] != -1):
        return dp[idx][prev_idx + 1]
 
    notTake = 0 + f(idx + 1, prev_idx, n, a, dp)
    take = -sys.maxsize -1
    if (prev_idx == -1 or a[idx] > a[prev_idx]):
        take = 1 + f(idx + 1, idx, n, a, dp)
 
    dp[idx][prev_idx + 1] = max(take, notTake)
    return dp[idx][prev_idx + 1]
 
# Function to find length of longest increasing
# subsequence.
def longestSubsequence(n, a):
 
    dp = [[-1 for i in range(n + 1)]for j in range(n + 1)]
    return f(0, -1, n, a, dp)
 
# Driver program to test above function
a = [ 3, 10, 2, 1, 20 ]
n = len(a)
print("Length of lis is ",longestSubsequence(n, a))
 
# This code is contributed by shinjanpatra
Producción

Length of lis is 3

Análisis de Complejidad:

Complejidad Temporal: O(n 2 ). 
Espacio Auxiliar: O(n 2 ). 
 

Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *