Ever picked the biggest coin first and ended up with more coins? Let's see why and how to fix it!
Greedy vs DP How to Know Which to Apply in DSA Javascript - Why the Distinction Matters
Imagine you have a big jar of mixed coins and you want to give exact change using the fewest coins possible. You try picking the biggest coin first every time, but sometimes it doesn't work out perfectly.
Trying to pick the biggest coin each time (greedy) can fail when the coin sizes don't fit perfectly. You might end up with more coins than needed or no solution at all. Doing this by hand is slow and confusing.
Greedy algorithms pick the best choice at each step quickly but only work when the problem has a special structure. Dynamic Programming (DP) tries all possibilities smartly by remembering past results, ensuring the best overall solution.
function getChange(coins, amount) {
let count = 0;
coins.sort((a, b) => b - a);
for (let coin of coins) {
while (amount >= coin) {
amount -= coin;
count++;
}
}
return amount === 0 ? count : -1;
}function getChangeDP(coins, amount) {
const dp = Array(amount + 1).fill(Infinity);
dp[0] = 0;
for (let i = 1; i <= amount; i++) {
for (let coin of coins) {
if (coin <= i) dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
return dp[amount] === Infinity ? -1 : dp[amount];
}Understanding when to use Greedy or DP lets you solve problems efficiently and correctly, avoiding wasted effort and wrong answers.
Choosing the fastest route on a map: Greedy picks the closest next city, but DP considers all routes to find the shortest total path.
Greedy is fast but only works when local choices lead to global best.
DP is slower but guarantees the best solution by exploring all options smartly.
Knowing which to use saves time and avoids mistakes.
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, known as the greedy choice property.Step 2: Compare with dynamic programming conditions
Dynamic programming is used when problems have overlapping subproblems and optimal substructure, but greedy algorithms require only the greedy choice property and optimal substructure.Final Answer:
When the problem has the greedy choice property and optimal substructure -> Option DQuick Check:
Greedy choice property = Use greedy [OK]
- Confusing overlapping subproblems as greedy condition
- Using dynamic programming when greedy suffices
- Ignoring greedy choice property
Solution
Step 1: Understand Array initialization
Using new Array(n).fill(-1) creates an array of length n filled with -1, suitable for memoization since -1 indicates uncomputed.Step 2: Check other options
A and C fill with 0, which conflicts if 0 is a valid result; map is also unnecessary. D is invalid syntax.Final Answer:
const memo = new Array(n).fill(-1); -> Option AQuick Check:
Use new Array(n).fill(value) for memo [OK]
- Using map on uninitialized array
- Wrong order of fill arguments
- Using Array.fill which is invalid
function maxSum(arr) {
let incl = arr[0];
let excl = 0;
for (let i = 1; i < arr.length; i++) {
let new_excl = Math.max(incl, excl);
incl = excl + arr[i];
excl = new_excl;
}
return Math.max(incl, excl);
}
console.log(maxSum([3, 2, 7, 10]));Solution
Step 1: Trace variables for each element
Start: incl=3, excl=0 i=1: new_excl=max(3,0)=3; incl=0+2=2; excl=3 i=2: new_excl=max(2,3)=3; incl=3+7=10; excl=3 i=3: new_excl=max(10,3)=10; incl=3+10=13; excl=10Step 2: Calculate final max
Return max(incl, excl) = max(13, 10) = 13Final Answer:
17 -> Option AQuick Check:
Max sum non-adjacent = 17 [OK]
- Adding adjacent elements mistakenly
- Mixing incl and excl updates
- Misreading problem as max sum of all elements
function coinChange(coins, amount) {
coins.sort();
let count = 0;
for (let i = 0; i < coins.length; i++) {
while (amount >= coins[i]) {
amount -= coins[i];
count++;
}
}
return amount === 0 ? count : -1;
}
console.log(coinChange([1, 3, 4], 6));Solution
Step 1: Check sorting order
coins.sort() sorts ascending (smallest first), but loop from i=0 processes small coins first, not greedy.Step 2: Fix sorting for greedy approach
Use coins.sort((a,b) => b - a) to sort descending numerically, so largest coins used first.Final Answer:
Sort coins in descending order using coins.sort((a,b) => b - a) -> Option CQuick Check:
Correct numeric descending sort fixes greedy coin change [OK]
- Using default sort causing wrong order
- Changing loop logic unnecessarily
- Ignoring amount check in return
const tasks = [
{start: 1, end: 3},
{start: 2, end: 5},
{start: 4, end: 7},
{start: 6, end: 9}
];Choose the best method to solve this scheduling problem.
Solution
Step 1: Understand problem type
This is the classic activity selection problem, where we want the maximum number of non-overlapping intervals.Step 2: Identify best approach
Greedy algorithm works best by sorting tasks by earliest end time and selecting tasks that start after the last selected task ends.Final Answer:
Use a greedy algorithm by sorting tasks by earliest end time -> Option BQuick Check:
Greedy by earliest end time solves scheduling [OK]
- Trying all subsets with DP unnecessarily
- Sorting by start time instead of end time
- Picking longest tasks first causes overlaps
Ready to prove it?
Pass the test and this pattern moves unknown
