Can you tell when a quick choice is enough or when you need to think deeper to get the best answer?
Greedy vs DP How to Know Which to Apply in DSA Typescript - Why the Distinction Matters
Imagine you have a big jar of coins and you want to give exact change using the fewest coins possible. You try picking the biggest coin first every time, but sometimes this doesn't work and you end up with more coins than needed.
Picking coins greedily (always the biggest coin first) can be fast but sometimes leads to wrong answers. Trying every possible combination manually is slow and confusing. Without a clear method, you waste time and make mistakes.
Greedy and Dynamic Programming (DP) are smart ways to solve such problems. Greedy works when local best choices lead to global best. DP tries all options but remembers results to avoid repeating work. Knowing when to use each saves time and gets correct answers.
function getChange(coins: number[], amount: number): number {
let count = 0;
while (amount > 0) {
let coin = coins.reduce((max, c) => c <= amount ? Math.max(max, c) : max, 0);
if (coin === 0) return -1;
amount -= coin;
count++;
}
return count;
}function getChangeDP(coins: number[], amount: number): number {
const dp = Array(amount + 1).fill(Infinity);
dp[0] = 0;
for (let i = 1; i <= amount; i++) {
for (const coin of coins) {
if (coin <= i) dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
return dp[amount] === Infinity ? -1 : dp[amount];
}It enables you to solve complex optimization problems efficiently and correctly by choosing the right approach.
Choosing the best route for delivery trucks: Greedy might pick the closest next stop, but DP considers all routes to find the shortest total path.
Greedy is fast but only works if local choices lead to global best.
Dynamic Programming tries all options but remembers results to avoid repeated work.
Knowing when to use each saves time and ensures correct solutions.
Practice
Solution
Step 1: Understand greedy choice property
A greedy algorithm works when choosing the best option at each step leads to a global optimum.Step 2: Check optimal substructure
Optimal substructure means the optimal solution can be built from optimal solutions of subproblems.Final Answer:
When the problem has the greedy choice property and optimal substructure -> Option AQuick Check:
Greedy = greedy choice + optimal substructure [OK]
- Confusing overlapping subproblems with greedy applicability
- Using greedy when problem needs full exploration
- Ignoring optimal substructure requirement
Solution
Step 1: Understand memo type for DP
Memoization stores results with keys, so Record is suitable.Step 2: Check syntax correctness
const memo: Record = {}; correctly declares an empty object with number keys and values.Final Answer:
const memo: Record = {}; -> Option CQuick Check:
Memo object uses Record type [OK]
- Using var instead of const or let
- Incorrect type assertion for memo
- Using Map without type parameters
function maxSum(arr: number[]): number {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
if (arr[i] > 0) sum += arr[i];
}
return sum;
}
console.log(maxSum([-1, 2, 3, -5, 4]));
What is the output?Solution
Step 1: Trace the loop and sum positive numbers
Positive numbers are 2, 3, and 4. Sum = 2 + 3 + 4 = 9.Step 2: Confirm return value
The function returns sum = 9, so console.log prints 9.Final Answer:
9 -> Option AQuick Check:
Sum positives = 9 [OK]
- Adding negative numbers mistakenly
- Returning zero instead of sum
- Confusing output with array length
function fib(n: number, memo: Record = {}): number {
if (n <= 1) return n;
if (memo[n]) return memo[n];
memo[n] = fib(n - 1, memo) + fib(n - 2, memo);
return memo[n];
}
console.log(fib(5));Solution
Step 1: Analyze memo check condition
Checking 'if (memo[n])' fails when memo[n] is 0, which is falsy.Step 2: Correct memo check
Use 'if (memo[n] !== undefined)' to correctly detect stored values including zero.Final Answer:
The memo check should be 'if (memo[n] !== undefined)' to handle zero -> Option DQuick Check:
Memo check must handle zero values [OK]
- Using truthy check for memo causing wrong cache hits
- Wrong base case for Fibonacci
- Incorrect recursive call directions
interface Task {
duration: number;
deadline: number;
}
function scheduleTasks(tasks: Task[]): number {
// Implementation needed
}Solution
Step 1: Understand problem goal
Maximize number of tasks completed before deadlines means scheduling tasks efficiently.Step 2: Identify best approach
Greedy algorithm sorting by earliest deadline ensures maximum tasks fit before deadlines.Final Answer:
Use a greedy algorithm sorting tasks by earliest deadline to maximize count -> Option BQuick Check:
Earliest deadline first greedy maximizes tasks [OK]
- Trying all subsets with DP causing inefficiency
- Sorting by longest duration wastes time
- Ignoring deadlines in scheduling
Ready to prove it?
Pass the test and this pattern moves unknown
