0% found this document useful (0 votes)
14 views2 pages

Dynamic Programming & Greedy Techniques Guide

The document provides an overview of Dynamic Programming (DP) and Greedy Techniques, explaining their definitions, key concepts, and common examples. It outlines the steps to solve DP problems and contrasts DP with Greedy methods in terms of subproblem reuse, time complexity, and optimality. Additionally, it offers guidance on when to use DP versus Greedy approaches and lists practice problems for both techniques.

Uploaded by

Eyob Adamu
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views2 pages

Dynamic Programming & Greedy Techniques Guide

The document provides an overview of Dynamic Programming (DP) and Greedy Techniques, explaining their definitions, key concepts, and common examples. It outlines the steps to solve DP problems and contrasts DP with Greedy methods in terms of subproblem reuse, time complexity, and optimality. Additionally, it offers guidance on when to use DP versus Greedy approaches and lists practice problems for both techniques.

Uploaded by

Eyob Adamu
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Dynamic Programming & Greedy Technique - Notes

DYNAMIC PROGRAMMING (DP)

Definition:
Dynamic Programming is a method for solving complex problems by breaking them down into simpler subproblems and
storing the results of already solved subproblems to avoid redundant work.

Key Concepts:
1. Overlapping Subproblems
Example: Fibonacci sequence
2. Optimal Substructure
Example: Shortest path in a graph
3. Memoization (Top-Down)
Store results in a table (usually recursion + cache)
4. Tabulation (Bottom-Up)
Build table from smallest subproblem up to the final solution

Steps to Solve a DP Problem:


1. Define the state (dp[i] or dp[i][j])
2. Define the recurrence relation
3. Identify base cases
4. Implement using top-down (memoization) or bottom-up (tabulation)
5. Optimize space if possible

Common Examples:
- Fibonacci Numbers - 1D DP: dp[n] = dp[n-1] + dp[n-2]
- 0/1 Knapsack - 2D DP: dp[i][w] = max(dp[i-1][w], value + dp[i-1][w-weight])
- Longest Common Subsequence - 2D DP: dp[i][j] = dp[i-1][j-1] + 1 (if match)
- Longest Increasing Subsequence - 1D DP
- Matrix Chain Multiplication - Interval DP
- Coin Change - 1D DP: dp[i] += dp[i - coin] or dp[i] = min(dp[i], 1 + dp[i - coin])
- Edit Distance - 2D DP: Insert, delete, or replace

GREEDY TECHNIQUE

Definition:
Greedy algorithms make locally optimal choices at each step, hoping to find a global optimum.

Characteristics:
1. Greedy Choice Property
2. No need to solve all subproblems
3. Faster and easier, but only works if the problem has the greedy property

Common Greedy Examples:


- Activity Selection - Sort by end time
- Fractional Knapsack - Take max value/weight ratio first
- Huffman Encoding - Merge lowest frequency
- Job Sequencing - Sort by profit and deadline
- Coin Change - Only works when denominations are canonical
- Kruskal's Algorithm - Minimum Spanning Tree
Dynamic Programming & Greedy Technique - Notes

- Prim's Algorithm - MST using priority queue


- Dijkstra's Algorithm - Shortest path using greedy strategy

DP vs Greedy:
- Subproblem Reuse: DP - Yes, Greedy - No
- Time Complexity: DP - Higher, Greedy - Lower
- Optimal for all cases: DP - Yes, Greedy - Not always
- Use Case: DP - overlapping subproblems, Greedy - greedy property

How to Decide Between DP and Greedy?


- Use DP if optimal substructure and overlapping subproblems exist
- Use Greedy if local optimum leads to global optimum
- Test greedy on small input to verify

Practice Problems:
Dynamic Programming:
- Fibonacci
- 0/1 Knapsack
- Longest Common Subsequence
- Coin Change
- Edit Distance
- Matrix Chain Multiplication

Greedy:
- Activity Selection
- Fractional Knapsack
- Job Sequencing
- Huffman Coding
- Kruskal's and Prim's Algorithm

Common questions

Powered by AI

Tabulation (bottom-up approach) involves iteratively building up a table of results from smaller subproblems to solve larger ones. The primary benefit is that it often uses less memory because it builds on fixed space, potentially discarding unused results as it progresses . Moreover, it avoids the overhead of recursive function calls and can sometimes lead to simpler code for problems with a straightforward iterative structure. The drawback is that it typically requires a clear understanding of all subproblems beforehand and might compute unnecessary states that will not be needed for the final solution .

When deciding between a top-down (memoization) and bottom-up (tabulation) approach, several factors are key. Memoization is often preferred when direct recursive formulations are more intuitive and when potentially unnecessary states can be skipped thanks to on-demand computation, thus saving space and time . However, it can suffer from function call overhead and stack depth limitations. Tabulation may be more efficient in terms of runtime for problems with well-defined and manageable state spaces since it avoids recursion, processes in fixed space, and results are readily available without recursive lookups . The choice often depends on the problem specifics and which approach offers clearer, more efficient code for all necessary subproblems.

The 0/1 Knapsack problem is optimally solved using dynamic programming because it requires evaluating all possible combinations of selected items to ensure the maximum total value for a given capacity, given the constraint of selecting entire items (either included or not). Using greedy would fail here because selecting items based on value-to-weight ratio might lead to suboptimal solutions if items must be fully included or excluded. In contrast, the Fractional Knapsack problem effectively uses a greedy approach, selecting items based on the maximum value-to-weight ratio, and is optimal because items can be broken into smaller parts, perfectly exploiting the greedy property for maximum value .

Dynamic programming can optimize space complexity over traditional recursive methods by replacing recursive calls with iterative processes that store previous intermediate results in a table, such as in bottom-up tabulation. This allows for the results of subproblems to be reused without maintaining a large call stack, as typically required in naive recursion . Additionally, space optimization techniques in dynamic programming involve minimizing the storage of only necessary states at any given point, for example, by using rolling arrays or similar space-efficient data structures to overwrite unnecessary data as computation progresses .

Dynamic programming algorithms typically have higher time complexity because they systematically explore and store solutions for all possible subproblems to ensure an optimal solution is found, even when this means handling a large number of states or iterations through recursive or iterative processes . This comprehensive search is necessary for problems with overlapping subproblems and optimal substructure. In contrast, greedy algorithms often operate in linear or linearithmic time by making a single pass through data or resolving decisions at each step, avoiding the exhaustive search and storage that characterizes dynamic programming .

The presence of overlapping subproblems heavily influences the decision to apply dynamic programming over greedy algorithms. Overlapping subproblems mean that the solution to one part of the problem is needed multiple times across different subproblems. Dynamic programming excels in such scenarios because it stores and reuses the results of these common subproblems, ensuring efficiency and limiting redundant calculations . Conversely, greedy algorithms typically do not leverage previous solutions, instead making choices based solely on immediate benefit, and thus might not be suitable or even feasible for problems with significant subproblem overlaps .

The edit distance problem illustrates the principles of dynamic programming by breaking down the problem of transforming one string into another into a series of subproblems that consider character insertions, deletions, or substitutions. The key steps in formulating its solution involve defining a state dp[i][j] that represents the minimum number of operations needed to convert the first i characters of one string to the first j characters of another . The recurrence relation reflects the operations possible—minimum of incrementing changes by considering adjacent modifications (insertion, deletion, substitution). Base cases are initialized where one string is empty, warranting straight insertions or deletions to reconcile the difference . This method ensures optimal and complete evaluation by efficiently managing overlapping transformations.

The greedy technique is more efficient than dynamic programming in scenarios where the problem fundamentally possesses the greedy choice property, meaning that local optimizations directly lead to a global optimum. This can lead to much faster and simpler solutions, as seen in problems like the activity selection, where sorting by end time naturally leads to the optimal number of activities without considering all possibilities . However, the inherent risk lies in incorrectly assuming the problem has the greedy property, which might result in suboptimal solutions, as greedy algorithms don’t systematically explore all possible configurations like dynamic programming does .

Defining the state and recurrence relation for a dynamic programming problem is challenging due to the necessity of capturing the essence of the subproblem in a way that facilitates both thorough exploration and efficient reuse. The state must fully represent the subproblem in terms of its constraints and variables, often requiring nuanced insight into the problem structure to avoid redundancy and ensure comprehensive coverage . The recurrence relation must tightly couple previous states to subsequent results, accurately reflecting how each subproblem progressively contributes towards the final solution without oversimplification. Poor definitions can lead to complex, inefficient state spaces, resulting in excessive computation or even incorrect results if subproblems are misinterpreted or improperly connected .

Dynamic programming (DP) involves subproblem reuse as it systematically breaks down a problem into smaller overlapping subproblems and stores their solutions to avoid redundant calculations. This reuse is critical for optimization problems that exhibit overlapping subproblems and an optimal substructure, as it allows for efficient solution computation by preventing repeated work . In contrast, greedy techniques do not reuse subproblems as they make the locally optimal choice at each step without considering prior states, which can lead to faster solutions if the problem exhibits the greedy choice property, but can also fail to find a globally optimal solution if it doesn’t .

You might also like