@@ -11,7 +11,8 @@ Dynamic Programming
11
11
The main idea behind dynamic programming is to store the results of subproblems to avoid redundant computations.
12
12
This technique is known as "memoization" when done top-down (recursively) and "tabulation" when done bottom-up (iteratively).
13
13
14
- 1. Key Concepts in Dynamic Programming
14
+ 1. Key Concepts in Dynamic Programming:
15
+ ---------------------------------------
15
16
16
17
1. Optimal Substructure:
17
18
- A problem exhibits optimal substructure if an optimal solution to the problem can be constructed from optimal solutions
@@ -21,7 +22,8 @@ Dynamic Programming
21
22
- A problem has overlapping subproblems if the same subproblems are solved multiple times during the computation of the
22
23
overall problem.
23
24
24
- 2. Steps to Apply Dynamic Programming
25
+ 2. Steps to Apply Dynamic Programming:
26
+ --------------------------------------
25
27
26
28
1. Characterize the Structure of an Optimal Solution:
27
29
- Understand how to construct the optimal solution using solutions to subproblems.
@@ -36,7 +38,8 @@ Dynamic Programming
36
38
4. Construct the Optimal Solution (if needed):
37
39
- Trace back the stored results to construct the actual solution.
38
40
39
- 3. Examples of Dynamic Programming
41
+ 3. Examples of Dynamic Programming:
42
+ -----------------------------------
40
43
41
44
1. Fibonacci Sequence:
42
45
- The Fibonacci sequence can be defined as F(n) = F(n-1) + F(n-2) with base cases F(0) = 0 and F(1) = 1.
@@ -53,15 +56,17 @@ Dynamic Programming
53
56
- Dynamic programming can be used to fill a table where each entry L[i][j] represents the length of the LCS of the
54
57
first i elements of one sequence and the first j elements of the other.
55
58
56
- 4. Advantages of Dynamic Programming
59
+ 4. Advantages of Dynamic Programming:
60
+ -------------------------------------
57
61
58
62
- Efficiency: By storing the results of subproblems, dynamic programming reduces the time complexity of solving complex problems.
59
63
- Optimization: It guarantees finding the optimal solution if the problem has optimal substructure and overlapping subproblems.
60
64
61
- 5. Disadvantages of Dynamic Programming
65
+ 5. Disadvantages of Dynamic Programming:
66
+ ----------------------------------------
62
67
63
- - Space Complexity: Storing all subproblem results can require significant memory.
64
- - Complexity in Implementation: Understanding and correctly applying dynamic programming can be challenging.
68
+ - Space Complexity: Storing all subproblem results can require significant memory.
69
+ - Complexity in Implementation: Understanding and correctly applying dynamic programming can be challenging.
65
70
66
71
Dynamic programming is a powerful tool for solving a wide range of problems, particularly those involving optimization and
67
72
recursive substructure.
@@ -73,114 +78,118 @@ Dynamic Programming
73
78
Both approaches aim to solve complex problems by breaking them down into simpler subproblems, but they differ in how they handle
74
79
the subproblems and store intermediate results.
75
80
76
- 1. Top-Down Approach (Memoization)
81
+ 1. Top-Down Approach (Memoization):
82
+ -----------------------------------
77
83
78
84
The top-down approach involves solving the problem recursively and storing the results of subproblems to avoid redundant computations.
79
85
This is known as memoization.
80
86
81
- * Steps in Top-Down Approach:
87
+ * Steps in Top-Down Approach:
88
+ -----------------------------
82
89
83
- 1. Recursive Formulation:
84
- - Define the problem recursively in terms of smaller subproblems.
90
+ 1. Recursive Formulation:
91
+ - Define the problem recursively in terms of smaller subproblems.
85
92
86
- 2. Memoization:
87
- - Use a data structure (usually a dictionary or an array) to store the results of subproblems.
93
+ 2. Memoization:
94
+ - Use a data structure (usually a dictionary or an array) to store the results of subproblems.
88
95
89
- 3. Check and Reuse:
90
- - Before solving a subproblem, check if its result is already computed and stored. If so, reuse the stored result.
96
+ 3. Check and Reuse:
97
+ - Before solving a subproblem, check if its result is already computed and stored. If so, reuse the stored result.
91
98
92
- 4. Base Cases:
93
- - Define the base cases to stop the recursion.
99
+ 4. Base Cases:
100
+ - Define the base cases to stop the recursion.
94
101
95
- * Example (Fibonacci Sequence):
102
+ * Example (Fibonacci Sequence):
103
+ -------------------------------
96
104
97
- In the top-down approach, we use recursion and store the results of subproblems to avoid redundant computations.
98
- This involves using a data structure like an array or a HashMap to store intermediate results.
105
+ In the top-down approach, we use recursion and store the results of subproblems to avoid redundant computations.
106
+ This involves using a data structure like an array or a HashMap to store intermediate results.
99
107
100
- import java.util.HashMap;
101
- import java.util.Map;
108
+ import java.util.HashMap;
109
+ import java.util.Map;
102
110
103
- public class FibonacciTopDown {
104
- private Map<Integer, Integer> memo = new HashMap<>();
111
+ public class FibonacciTopDown {
112
+ private Map<Integer, Integer> memo = new HashMap<>();
105
113
106
- public int fib(int n) {
107
- if (memo.containsKey(n)) {
108
- return memo.get(n);
109
- }
110
- if (n <= 1) {
111
- return n;
112
- }
113
- int result = fib(n - 1) + fib(n - 2);
114
- memo.put(n, result);
115
- return result;
116
- }
117
-
118
- public static void main(String[] args) {
119
- FibonacciTopDown fibonacci = new FibonacciTopDown();
120
- System.out.println(fibonacci.fib(10)); // Output: 55
121
- }
122
- }
114
+ public int fib(int n) {
115
+ if (memo.containsKey(n)) {
116
+ return memo.get(n);
117
+ }
118
+ if (n <= 1) {
119
+ return n;
120
+ }
121
+ int result = fib(n - 1) + fib(n - 2);
122
+ memo.put(n, result);
123
+ return result;
124
+ }
123
125
126
+ public static void main(String[] args) {
127
+ FibonacciTopDown fibonacci = new FibonacciTopDown();
128
+ System.out.println(fibonacci.fib(10)); // Output: 55
129
+ }
130
+ }
124
131
125
- 2. Bottom-Up Approach (Tabulation)
132
+ 2. Bottom-Up Approach (Tabulation):
133
+ -----------------------------------
126
134
127
135
The bottom-up approach involves solving the problem iteratively, starting from the smallest subproblems and building
128
136
up to the solution of the original problem. This is known as tabulation.
129
137
130
- * Steps in Bottom-Up Approach:
131
-
132
- 1. Iterative Formulation:
133
- - Define the problem iteratively, starting from the smallest subproblems.
134
-
135
- 2. Table Initialization:
136
- - Use a data structure (usually an array) to store the results of subproblems.
137
-
138
- 3. Iterative Computation:
139
- - Compute the results of subproblems in a specific order, usually from the smallest to the largest.
140
-
141
- 4. Final Solution:
142
- - The final solution to the original problem is obtained from the last computed value in the table.
143
-
144
- * Example (Fibonacci Sequence):
145
-
146
- In the bottom-up approach, we iteratively solve the problem from the smallest subproblems up to the original problem,
147
- storing intermediate results in a data structure like an array.
148
-
149
- public class FibonacciBottomUp {
150
- public int fib(int n) {
151
- if (n <= 1) {
152
- return n;
138
+ * Steps in Bottom-Up Approach:
139
+ ------------------------------
140
+
141
+ 1. Iterative Formulation:
142
+ - Define the problem iteratively, starting from the smallest subproblems.
143
+
144
+ 2. Table Initialization:
145
+ - Use a data structure (usually an array) to store the results of subproblems.
146
+
147
+ 3. Iterative Computation:
148
+ - Compute the results of subproblems in a specific order, usually from the smallest to the largest.
149
+
150
+ 4. Final Solution:
151
+ - The final solution to the original problem is obtained from the last computed value in the table.
152
+
153
+ * Example (Fibonacci Sequence):
154
+ -------------------------------
155
+
156
+ In the bottom-up approach, we iteratively solve the problem from the smallest subproblems up to the original problem,
157
+ storing intermediate results in a data structure like an array.
158
+
159
+ public class FibonacciBottomUp {
160
+ public int fib(int n) {
161
+ if (n <= 1) {
162
+ return n;
163
+ }
164
+ int[] dp = new int[n + 1];
165
+ dp[0] = 0;
166
+ dp[1] = 1;
167
+ for (int i = 2; i <= n; i++) {
168
+ dp[i] = dp[i - 1] + dp[i - 2];
169
+ }
170
+ return dp[n];
171
+ }
172
+
173
+ public static void main(String[] args) {
174
+ FibonacciBottomUp fibonacci = new FibonacciBottomUp();
175
+ System.out.println(fibonacci.fib(10)); // Output: 55
176
+ }
153
177
}
154
- int[] dp = new int[n + 1];
155
- dp[0] = 0;
156
- dp[1] = 1;
157
- for (int i = 2; i <= n; i++) {
158
- dp[i] = dp[i - 1] + dp[i - 2];
159
- }
160
- return dp[n];
161
- }
162
-
163
- public static void main(String[] args) {
164
- FibonacciBottomUp fibonacci = new FibonacciBottomUp();
165
- System.out.println(fibonacci.fib(10)); // Output: 55
166
- }
167
- }
168
-
169
178
170
179
* Comparison of Top-Down and Bottom-Up Approaches
171
180
172
- - Top-Down (Memoization):
173
- - Uses recursion and stores intermediate results.
174
- - More intuitive for problems naturally expressed in recursive form.
175
- - May lead to high recursion depth and potential stack overflow for very large problems.
176
- - Can be easier to implement if the recursive solution is straightforward.
177
-
178
- - Bottom-Up (Tabulation):
179
- - Uses iteration and builds up solutions from smaller subproblems.
180
- - Typically more space-efficient because it can often be optimized to use a fixed amount of space (e.g., only
181
- storing the last two values for Fibonacci).
182
- - Avoids recursion depth issues.
183
- - Can be more complex to implement if the iterative solution is not obvious.
181
+ - Top-Down (Memoization):
182
+ - Uses recursion and stores intermediate results.
183
+ - More intuitive for problems naturally expressed in recursive form.
184
+ - May lead to high recursion depth and potential stack overflow for very large problems.
185
+ - Can be easier to implement if the recursive solution is straightforward.
186
+
187
+ - Bottom-Up (Tabulation):
188
+ - Uses iteration and builds up solutions from smaller subproblems.
189
+ - Typically more space-efficient because it can often be optimized to use a fixed amount of space (e.g., only
190
+ storing the last two values for Fibonacci).
191
+ - Avoids recursion depth issues.
192
+ - Can be more complex to implement if the iterative solution is not obvious.
184
193
185
194
Both approaches ultimately achieve the same goal: solving the problem efficiently by leveraging the solutions to subproblems.
186
195
The choice between top-down and bottom-up depends on the specific problem and the programmer's preference or constraints.
0 commit comments