In Dynamic Programming | Set 1 (overlapping Subproblems property) and Dynamic programming | In Set 2 (Optimal substructure property) We have discussed overlapping sub-problems and optimal substructure properties, and now we look at a problem that can be solved using dynamic programming: The Longest ascending subsequence (longest increasing Subsequence (LIS)).
The longest ascending subsequence problem, which is devoted to finding the longest subsequence in a given sequence, in ascending order of the elements in that subsequence. For example, the longest ascending subsequence of the sequence {10, 22, 9, 33, 21, 50, 41, 60, 80} has a length of 6, and the longest ascending subsequence is {10, 22, 33, 50, 60, 80}.
Optimal substructure:
Suppose arr[0..n-1] is an input array,L (i) is the length of the longest ascending subsequence of an array with the following subscript i , satisfying Arr[i] is part of the LIS, i.e. arr[i ] is the last element in the LIS, then L (i) can be recursively represented as:
L (i) = {1 + Max (L (j))} where J < I and Arr[j] < Arr[i] and if there is no such J then L (i) = 1
To get the length of a given array of LIS, we need to return max (L (i)) where 0 < i < n.
Therefore, the LIS problem has the optimal substructure property, so the problem can be solved by using the sub-problem method.
Overlapping subproblems:
The following is a simple recursive version of the LIS problem.
/* A Naive Recursive implementation of LIS problem */#include <stdio.h> #include <stdlib.h>/* To do use of rec Ursive calls, this function must return both things:1) Length of LIS ending with element arr[n-1]. We Use Max_ending_here-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 are stored in *max_ref which are our final result*/int _lis (int arr[], int n, int *MAX_REF) {/* Base case */if (n = = 1) return 1; int res, max_ending_here = 1; Length of Lis ending with arr[n-1]/* Recursively get all LIS ending with arr[0], arr[1] ... ar[n-2]. If Arr[i-1] is smaller than arr[n-1], and Max ending with arr[n-1] needs to being 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\n", LIS (arr, n)); GetChar (); return 0;}
Consider the above implementation, as follows: When the array size is 4 o'clock recursive tree, Lis (n) is the last element with N, the length of the LIS of the array.
It is not difficult to find that there are sub-problems which are repeated. Therefore, the problem has overlapping substructure properties, which can be prevented by memoization or tabulation to prevent the iterative calculation of sub-problems. As below, is the tabluated realization of LIS problem.
/* Dynamic programming implementation of LIS problem */#include <stdio.h> #include <stdlib.h>/* LIS () returns t He length of the longest increasing subsequence in arr[] of size n */int lis (int arr[], int n) {int *lis, I, J, MA x = 0; Lis = (int*) malloc (sizeof (int) * n); /* 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]; /* Free memory to avoid memory leak */free (LIS); 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\n", LIS (arr, n)); GetChar (); return 0;}
Note that the time complexity required for the above dynamic programming solution is O (n^2), in fact the LIS problem has an O (NLOGN) solution (see this). In this case, we did not discuss the solution of O (Nlogn), here, just use this article as a simple example of dynamic programming.
Dynamic Programming | Set 3 (Longest increasing subsequence)