Let us discuss Longest Increasing Subsequence (LIS) problem as an example problem that can be solved using Dynamic Programming.
The longest Increasing Subsequence (LIS) problem is to find the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order. For example, length of LIS for { 10, 22, 9, 33, 21, 50, 41, 60, 80 } is 6 and LIS is {10, 22, 33, 50, 60, 80}.
Optimal Substructure:
Let arr[0..n-1] be the input array and L(i) be the length of the LIS till index i such that arr[i] is part of LIS and arr[i] is the last element in LIS, then L(i) can be recursively written 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 LIS of a given array, we need to return max(L(i)) where 0 < i < n
So the LIS problem has optimal substructure property as the main problem can be solved using solutions to subproblems.
Overlapping Subproblems:
Following is simple recursive implementation of the LIS problem. The implementation simply follows the recursive structure mentioned above. The value of lis ending with every element is returned using max_ending_here. The overall lis is returned using pointer to a variable max.
package DP; import java.util.Arrays; // 最长递增子序列-Longest Increasing Subsequence /** Let arr[0..n-1] be the input array and L(i) be the length of the LIS till index i such that arr[i] is part of LIS and arr[i] is the last element in LIS 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 LIS of a given array, we need to return max(L(i)) where 0 < i < n http://www.geeksforgeeks.org/dynamic-programming-set-3-longest-increasing-subsequence/ */ public class LIS { public static void main(String[] args) { int[] A = {10, 22, 9, 33, 21, 50, 41, 60}; System.out.println(lis(A)); System.out.println(lis(A, A.length)); } /* A Naive 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 */ public static int _lis(int arr[], int n, int[] maxRef){ if(n == 1){ /* Base case */ return 1; } int res, maxEndingHere = 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 be updated, then update it */ for(int i=1; i<n; i++){ res = _lis(arr, i, maxRef); if(arr[i-1] < arr[n-1] && res+1>maxEndingHere){ maxEndingHere = res + 1; } } // Compare max_ending_here with the overall max. And update the // overall max if needed if(maxRef[0] < maxEndingHere){ maxRef[0] = maxEndingHere; } // Return length of LIS ending with arr[n-1] return maxEndingHere; } public static int lis(int arr[], int n){ int[] max = {1}; _lis(arr, n, max); return max[0]; } //========================================== // Naive O(n2) DP solution // 看A[i]能接在哪些数字后面 public static int lis(int[] A){ int[] dp = new int[A.length]; // 对每一个元素存储它的lis Arrays.fill(dp, 1); // 初始化,每个数字本身就是长度为1的lis int max = Integer.MIN_VALUE; // 对每一个数字求其LIS for(int i=1; i<A.length; i++){ // 找出A[i]能接在哪些数字后面, // 如果可以接,找出接完后长度最大那个 for(int j=0; j<i; j++){ if(A[j]<A[i]){ // 只有符合递增规律的才能接 // 找出接完后长度最大那个 dp[i] = Math.max(dp[i], dp[j]+1); max = Math.max(max, dp[i]); } } } return max; } }