### Dynamic Programming

Dynamic Programming (DP) is a powerful algorithmic optimization technique used to solve problems by
breaking them down into simpler subproblems and storing the solutions to these subproblems. This
approach allows for efficient computation by avoiding redundant calculations through memoization or
bottom-up tabulation.

DP is characterized by two key properties: overlapping subproblems and optimal substructure. Overlapping
subproblems occur when the same subproblem is encountered multiple times during the computation. Optimal
substructure implies that the optimal solution to a larger problem can be constructed from optimal
solutions to its subproblems.

Two main approaches to DP include:

**Top-Down Approach (Memoization)**: In this approach, the problem is recursively divided into
subproblems, and the solutions to these subproblems are stored in a table to avoid redundant
calculations. This technique is particularly useful for problems with a recursive structure.

**Bottom-Up Approach (Tabulation)**: In this approach, solutions to subproblems are computed
iteratively starting from the smallest subproblems and working towards the larger problem. The results
are stored in a table, and each subsequent solution builds upon previously computed solutions. This
approach is often more efficient in terms of memory usage and computational time.

Key concepts in DP include:

**State Representation:** Defining the parameters that characterize a subproblem.

**Recurrence Relation:** Formulating a mathematical equation to express the relationship between
subproblems and their solutions.

**Initialization:** Setting base cases or initial values for the table entries.

**Transition:** Determining how to transition from smaller subproblems to larger ones.