Dynamic Programming (I)
1. Dynamic Programming
Generally speaking, Dynamic Programming (DP) is an algorithmic paradigm where we solve an optimization problem with optimal structure in a bottom-up way to avoid overlapping sub-problems. According to CLRS, a problem exhibits optimal structure if you need to find the solution to its sub-problems before accomplishing the whole optimization problem. In this sense, every problem that can be solved by DP is based on a DAG structure, where each node is a sub-problem and a sub-problem can be solved only if its preceding sub-problems have been solved.
We refuse to solve the DP problems in a recursive way in that we will be faced with a lot of overlapping sub-problems, which render such solution awfully inefficient. Usually, we choose to sacrifice space in order to save time when we exploit the optimal structure of an optimization problem. Moreover, there are some space compression techniques to minimize the cost of a solution.
2. Knapsack Problems
About the description of Knapsack Problems, please refer to wikipedia.
(1) Here is my solution to Knapsack with repetition:
1 public static int solve(int num, int vol) { 2 // Here we use DP to solve Unbounded Knapsack Problem 3 // Precondition: cost[] and value[] are the weights and values 4 // of the num objects respectively 5 // Postcondition: the maximum value that a knapsack with 6 // capacity vol can contain is returned 7 int[] dp = new int[vol+1]; 8 for (int i=0;i<=vol;i++){ 9 // Solve the problem in a bottom-up way, so that 10 // for all j<=i dp[j] will be the max value that a knapsack 11 // of capacity v can contain 12 for (int j=0;j<=num;j++){ 13 if (cost[j]<=i && dp[i-cost[j]]+value[j]>dp[i]) { 14 dp[i] = dp[i-cost[j]]+value[j]; 15 } 16 } 17 } 18 return dp[vol]; 19 }
(2) This is my solution to Knapsack without repetition:
1 public static int solve(int num,int vol) { 2 // Here we use DP to solve 0 - 1 Knapsack Problem 3 // Precondition: cost[] and value[] are the weights and values 4 // of the num objects respectively 5 // Postcondition: the maximum value that a knapsack with 6 // capacity vol can contain is returned 7 int [][] dp = new int[vol+1][num+1]; 8 // dp[i][j] will be the max value that a knapsack of capacity i 9 // can contain by choosing objects from 0 to j-1 10 // Basic Cases: dp[i][j] = 0, for all (i==0||j==0) 11 for (int j=1;j<=num;j++) { 12 for (int i=1;i<=vol;i++) { 13 dp[i][j] = dp[i][j-1]; 14 if (cost[j-1]<=i && dp[i-cost[j-1]][j-1]+value[j-1]>dp[i][j]) { 15 dp[i][j] = dp[i-cost[j-1]][j-1]+value[j-1]; 16 } 17 } 18 } 19 return dp[vol][num]; 20 }
3. Maximum Sum of Consecutive Subsequence
Many dynamic programming problems relies on a recursive structure like sequences, such as the classical Longest Common Subsequence introduced in CLRS. In this section, I wish to offer my solution to POJ 2479, a problem that requires to calculate the maximum sum among all the consecutive sub-sequences of a given integer sequence.
1 import java.util.*; 2 3 public class Main { 4 public static Scanner in; 5 public static int num; 6 public static int [][] dp; 7 8 public static long maxSum() { 9 for (int i=1;i<num;i++) { 10 // Invariant: for j<=i, dp[j][0] will be the max 11 // sum of all the sub-sequences ending at j 12 if (dp[i-1][0]>0) { 13 dp[i][0]+=dp[i-1][0]; 14 } 15 } 16 for (int i=1;i<num;i++) { 17 // Invariant: for j<=i, dp[j][0] will be the max 18 // sum of all the sub-sequences whose ending 19 // index is no larger than j 20 if (dp[i-1][0]>dp[i][0]) { 21 dp[i][0] = dp[i-1][0]; 22 } 23 } 24 for (int i=num-2;i>=0;i--) { 25 // Invariant: for j>=i, dp[j][0] will be the max 26 // sum of all the sub-sequences starting at j 27 if (dp[i+1][1]>0) { 28 dp[i][1]+=dp[i+1][1]; 29 } 30 } 31 for (int i=num-2;i>=0;i--) { 32 // Invariant: for j>=i, dp[j][0] will be the max 33 // sum of all the sub-sequences whose starting 34 // index is no less than j 35 if (dp[i+1][1]>dp[i][1]) { 36 dp[i][1] = dp[i+1][1]; 37 } 38 } 39 int val = dp[0][0]+dp[num-1][1]; 40 for (int i=1;i<num;i++) { 41 if (dp[i-1][0]+dp[i][1]>val) { 42 val = dp[i-1][0]+dp[i][1]; 43 } 44 } 45 return val; 46 } 47 public static void main(String[] args) { 48 in = new Scanner(System.in); 49 dp = new int [50000][2]; 50 int t = in.nextInt(); 51 String line; StringTokenizer str; 52 for (int i=0;i<t;i++) { 53 num = in.nextInt(); 54 in.nextLine(); // skip the newline symbol 55 line = in.nextLine(); // nextInt() is too time-consuming 56 str = new StringTokenizer(line); 57 for (int j=0;j<num;j++) { 58 dp[j][0] = dp[j][1] = Integer.parseInt(str.nextToken()); 59 } 60 System.out.println(maxSum()); 61 } 62 in.close(); 63 } 64 }
References:
1. Cormen, T. H. et al. Introduction to Algorithms[M].北京:机械工业出版社,2006-09
2. Dasgupta, Sanjoy, Christos Papadimitriou, and Umesh Vazirani. Algorithms[M].北京:机械工业出版社,2009-01-01