How to determine the longest increasing subsequence using dynamic programming?

前端 未结 19 2338
醉梦人生
醉梦人生 2020-11-22 10:55

I have a set of integers. I want to find the longest increasing subsequence of that set using dynamic programming.

相关标签:
19条回答
  • 2020-11-22 11:25
    def longestincrsub(arr1):
        n=len(arr1)
        l=[1]*n
        for i in range(0,n):
            for j in range(0,i)  :
                if arr1[j]<arr1[i] and l[i]<l[j] + 1:
                    l[i] =l[j] + 1
        l.sort()
        return l[-1]
    arr1=[10,22,9,33,21,50,41,60]
    a=longestincrsub(arr1)
    print(a)
    

    even though there is a way by which you can solve this in O(nlogn) time(this solves in O(n^2) time) but still this way gives the dynamic programming approach which is also good .

    0 讨论(0)
  • 2020-11-22 11:26

    here is java O(nlogn) implementation

    import java.util.Scanner;
    
    public class LongestIncreasingSeq {
    
    
        private static int binarySearch(int table[],int a,int len){
    
            int end = len-1;
            int beg = 0;
            int mid = 0;
            int result = -1;
            while(beg <= end){
                mid = (end + beg) / 2;
                if(table[mid] < a){
                    beg=mid+1;
                    result = mid;
                }else if(table[mid] == a){
                    return len-1;
                }else{
                    end = mid-1;
                }
            }
            return result;
        }
        
        public static void main(String[] args) {        
            
    //        int[] t = {1, 2, 5,9,16};
    //        System.out.println(binarySearch(t , 9, 5));
            Scanner in = new Scanner(System.in);
            int size = in.nextInt();//4;
            
            int A[] = new int[size];
            int table[] = new int[A.length]; 
            int k = 0;
            while(k<size){
                A[k++] = in.nextInt();
                if(k<size-1)
                    in.nextLine();
            }        
            table[0] = A[0];
            int len = 1; 
            for (int i = 1; i < A.length; i++) {
                if(table[0] > A[i]){
                    table[0] = A[i];
                }else if(table[len-1]<A[i]){
                    table[len++]=A[i];
                }else{
                    table[binarySearch(table, A[i],len)+1] = A[i];
                }            
            }
            System.out.println(len);
        }    
    }
    

    //TreeSet can be used

    0 讨论(0)
  • 2020-11-22 11:28

    OK, I will describe first the simplest solution which is O(N^2), where N is the size of the collection. There also exists a O(N log N) solution, which I will describe also. Look here for it at the section Efficient algorithms.

    I will assume the indices of the array are from 0 to N - 1. So let's define DP[i] to be the length of the LIS (Longest increasing subsequence) which is ending at element with index i. To compute DP[i] we look at all indices j < i and check both if DP[j] + 1 > DP[i] and array[j] < array[i] (we want it to be increasing). If this is true we can update the current optimum for DP[i]. To find the global optimum for the array you can take the maximum value from DP[0...N - 1].

    int maxLength = 1, bestEnd = 0;
    DP[0] = 1;
    prev[0] = -1;
    
    for (int i = 1; i < N; i++)
    {
       DP[i] = 1;
       prev[i] = -1;
    
       for (int j = i - 1; j >= 0; j--)
          if (DP[j] + 1 > DP[i] && array[j] < array[i])
          {
             DP[i] = DP[j] + 1;
             prev[i] = j;
          }
    
       if (DP[i] > maxLength)
       {
          bestEnd = i;
          maxLength = DP[i];
       }
    }
    

    I use the array prev to be able later to find the actual sequence not only its length. Just go back recursively from bestEnd in a loop using prev[bestEnd]. The -1 value is a sign to stop.


    OK, now to the more efficient O(N log N) solution:

    Let S[pos] be defined as the smallest integer that ends an increasing sequence of length pos. Now iterate through every integer X of the input set and do the following:

    1. If X > last element in S, then append X to the end of S. This essentialy means we have found a new largest LIS.

    2. Otherwise find the smallest element in S, which is >= than X, and change it to X. Because S is sorted at any time, the element can be found using binary search in log(N).

    Total runtime - N integers and a binary search for each of them - N * log(N) = O(N log N)

    Now let's do a real example:

    Collection of integers: 2 6 3 4 1 2 9 5 8

    Steps:

    0. S = {} - Initialize S to the empty set
    1. S = {2} - New largest LIS
    2. S = {2, 6} - New largest LIS
    3. S = {2, 3} - Changed 6 to 3
    4. S = {2, 3, 4} - New largest LIS
    5. S = {1, 3, 4} - Changed 2 to 1
    6. S = {1, 2, 4} - Changed 3 to 2
    7. S = {1, 2, 4, 9} - New largest LIS
    8. S = {1, 2, 4, 5} - Changed 9 to 5
    9. S = {1, 2, 4, 5, 8} - New largest LIS
    

    So the length of the LIS is 5 (the size of S).

    To reconstruct the actual LIS we will again use a parent array. Let parent[i] be the predecessor of element with index i in the LIS ending at element with index i.

    To make things simpler, we can keep in the array S, not the actual integers, but their indices(positions) in the set. We do not keep {1, 2, 4, 5, 8}, but keep {4, 5, 3, 7, 8}.

    That is input[4] = 1, input[5] = 2, input[3] = 4, input[7] = 5, input[8] = 8.

    If we update properly the parent array, the actual LIS is:

    input[S[lastElementOfS]], 
    input[parent[S[lastElementOfS]]],
    input[parent[parent[S[lastElementOfS]]]],
    ........................................
    

    Now to the important thing - how do we update the parent array? There are two options:

    1. If X > last element in S, then parent[indexX] = indexLastElement. This means the parent of the newest element is the last element. We just prepend X to the end of S.

    2. Otherwise find the index of the smallest element in S, which is >= than X, and change it to X. Here parent[indexX] = S[index - 1].

    0 讨论(0)
  • 2020-11-22 11:28

    The following C++ implementation includes also some code that builds the actual longest increasing subsequence using an array called prev.

    std::vector<int> longest_increasing_subsequence (const std::vector<int>& s)
    {
        int best_end = 0;
        int sz = s.size();
    
        if (!sz)
            return std::vector<int>();
    
        std::vector<int> prev(sz,-1);
        std::vector<int> memo(sz, 0);
    
        int max_length = std::numeric_limits<int>::min();
    
        memo[0] = 1;
    
        for ( auto i = 1; i < sz; ++i)
        {
            for ( auto j = 0; j < i; ++j)
            {
                if ( s[j] < s[i] && memo[i] < memo[j] + 1 )
                {
                    memo[i] =  memo[j] + 1;
                    prev[i] =  j;
                }
            }
    
            if ( memo[i] > max_length ) 
            {
                best_end = i;
                max_length = memo[i];
            }
        }
    
        // Code that builds the longest increasing subsequence using "prev"
        std::vector<int> results;
        results.reserve(sz);
    
        std::stack<int> stk;
        int current = best_end;
    
        while (current != -1)
        {
            stk.push(s[current]);
            current = prev[current];
        }
    
        while (!stk.empty())
        {
            results.push_back(stk.top());
            stk.pop();
        }
    
        return results;
    }
    

    Implementation with no stack just reverse the vector

    #include <iostream>
    #include <vector>
    #include <limits>
    std::vector<int> LIS( const std::vector<int> &v ) {
      auto sz = v.size();
      if(!sz)
        return v;
      std::vector<int> memo(sz, 0);
      std::vector<int> prev(sz, -1);
      memo[0] = 1;
      int best_end = 0;
      int max_length = std::numeric_limits<int>::min();
      for (auto i = 1; i < sz; ++i) {
        for ( auto j = 0; j < i ; ++j) {
          if (s[j] < s[i] && memo[i] < memo[j] + 1) {
            memo[i] = memo[j] + 1;
            prev[i] = j;
          }
        }
        if(memo[i] > max_length) {
          best_end = i;
          max_length = memo[i];
        }
      }
    
      // create results
      std::vector<int> results;
      results.reserve(v.size());
      auto current = best_end;
      while (current != -1) {
        results.push_back(s[current]);
        current = prev[current];
      }
      std::reverse(results.begin(), results.end());
      return results;
    }
    
    0 讨论(0)
  • 2020-11-22 11:30

    This is a Java implementation in O(n^2). I just did not use Binary Search to find the smallest element in S, which is >= than X. I just used a for loop. Using Binary Search would make the complexity at O(n logn)

    public static void olis(int[] seq){
    
        int[] memo = new int[seq.length];
    
        memo[0] = seq[0];
        int pos = 0;
    
        for (int i=1; i<seq.length; i++){
    
            int x = seq[i];
    
                if (memo[pos] < x){ 
                    pos++;
                    memo[pos] = x;
                } else {
    
                    for(int j=0; j<=pos; j++){
                        if (memo[j] >= x){
                            memo[j] = x;
                            break;
                        }
                    }
                }
                //just to print every step
                System.out.println(Arrays.toString(memo));
        }
    
        //the final array with the LIS
        System.out.println(Arrays.toString(memo));
        System.out.println("The length of lis is " + (pos + 1));
    
    }
    
    0 讨论(0)
  • 2020-11-22 11:32

    Here is a Scala implementation of the O(n^2) algorithm:

    object Solve {
      def longestIncrSubseq[T](xs: List[T])(implicit ord: Ordering[T]) = {
        xs.foldLeft(List[(Int, List[T])]()) {
          (sofar, x) =>
            if (sofar.isEmpty) List((1, List(x)))
            else {
              val resIfEndsAtCurr = (sofar, xs).zipped map {
                (tp, y) =>
                  val len = tp._1
                  val seq = tp._2
                  if (ord.lteq(y, x)) {
                    (len + 1, x :: seq) // reversely recorded to avoid O(n)
                  } else {
                    (1, List(x))
                  }
              }
              sofar :+ resIfEndsAtCurr.maxBy(_._1)
            }
        }.maxBy(_._1)._2.reverse
      }
    
      def main(args: Array[String]) = {
        println(longestIncrSubseq(List(
          0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15)))
      }
    }
    
    0 讨论(0)
提交回复
热议问题