By the end of this chapter you'll be able to…

  • 1State the single question that distinguishes the three techniques
  • 2Recognise when subproblems are independent versus overlapping
  • 3Write the recurrence for a divide-and-conquer algorithm
  • 4Explain why Strassen's exponent falls from 3 to 2.81
  • 5State both conditions dynamic programming requires
  • 6Explain why optimal substructure alone is insufficient
  • 7Explain the gap between distinct subproblems and recursive calls
  • 8Compare memoisation and tabulation on overhead, coverage and space
  • 9Apply rolling-array space optimisation to a tabulated program
  • 10Write the state and recurrence for 0/1 knapsack
  • 11Write the state and recurrence for LCS, edit distance and matrix chain
  • 12State the cost of each classic dynamic program
  • 13State both conditions greedy requires
  • 14Construct an exchange argument for a greedy choice
  • 15Explain why greedy fails on 0/1 knapsack with a counterexample
  • 16Explain why fractional knapsack is greedy but 0/1 is not
  • 17Prove that earliest finishing time is optimal for activity selection
  • 18State why earliest start and shortest duration both fail
  • 19Describe Huffman's greedy choice and why it is safe
  • 20State why Dijkstra requires non-negative weights
  • 21Apply the decision procedure for choosing among the three techniques
  • 22Explain why O(nW) is pseudo-polynomial rather than polynomial
💡
Why this chapter matters in GATE
The three design techniques are usually taught as three separate toolkits, but they are better understood as three answers to a single question: when a problem breaks into subproblems, what is the relationship between those subproblems? If they are independent, divide and conquer applies and nothing is computed twice. If they overlap, dynamic programming applies and storing results is the entire optimisation, because the same subproblem is reached by many paths. If one choice can be proved safe, greedy applies and there is only one subproblem to consider at each step. So the diagnostic is always two questions: do the subproblems overlap, and can I prove a choice is safe? Getting that wrong is what produces an exponential algorithm for a polynomial problem, or a fast algorithm that silently returns wrong answers, and both failures show up in GATE questions as constructed counterexamples.

Before you start — revise these

🔗
Asymptotic Complexity & Recurrences
Every divide-and-conquer cost is a master-theorem recurrence, and the pseudo-polynomial distinction rests on measuring input length in bits.
🔗
Searching, Sorting & Hashing
Merge sort and quicksort are the standard divide-and-conquer examples, and their recurrences are the template for the whole family.
🔗
Binary Heaps & Graphs as Data Structures
Dijkstra, Prim and Huffman are all greedy algorithms driven by a priority queue, which is what makes them efficient.

Algorithm Design: Greedy, Divide & Conquer, Dynamic Programming

The three design techniques are usually taught as three separate toolkits. They are better understood as three answers to a single question.

When a problem breaks into subproblems, what is the relationship between those subproblems?

If the subproblems are independent, use divide and conquer. Solve each once, combine the answers, and nothing is computed twice because nothing overlaps.

If the subproblems overlap, use dynamic programming. The same subproblem is reached by many different paths, so solving it repeatedly is exponentially wasteful and storing the answer is the entire optimisation.

If one choice is provably safe, use greedy. There is then only one subproblem to consider at each step, and no comparison of alternatives is needed at all.

So the diagnostic question is always: do the subproblems overlap, and can I prove a choice is safe? Answering those two settles which technique applies, and getting the answer wrong is what produces an exponential algorithm for a polynomial problem, or a fast algorithm that returns wrong answers.

1. Divide and Conquer

The pattern has three steps: divide the problem into subproblems, conquer them recursively, and combine their solutions.

The defining property is that the subproblems are disjoint. Merge sort's two halves share no elements, so no work is duplicated and the recursion tree has no repeated nodes.

The cost is a recurrence of the form , solved by the master theorem.

AlgorithmRecurrenceCost
Binary search
Merge sort
Quicksort (balanced)
Strassen
Closest pair

Strassen's algorithm is the standard illustration of why the branching factor matters. Naive matrix multiplication does 8 recursive multiplications of half-sized matrices, giving and the familiar . Strassen restructures the arithmetic to use only 7, giving .

The saving comes entirely from reducing from 8 to 7, at the cost of more additions — which are cheaper and do not affect the exponent.

2. Dynamic Programming

Dynamic programming applies when two conditions hold together.

Optimal substructure: an optimal solution contains optimal solutions to its subproblems. Overlapping subproblems: the same subproblem is solved many times by naive recursion.

Both are required. Optimal substructure alone permits divide and conquer; overlap alone without optimal substructure means the stored answers are not reusable.

The canonical demonstration is Fibonacci. Naive recursion computes twice, three times, and so on, giving exponential total work — yet there are only distinct subproblems. Storing each result makes the algorithm linear.

That gap between the number of distinct subproblems and the number of recursive calls is exactly what dynamic programming recovers.

Two implementations exist and are examined as a contrast.

Memoisation is top-down: write the natural recursion and cache each result the first time it is computed. It solves only the subproblems actually reachable, and its overhead is recursion and hashing.

Tabulation is bottom-up: fill a table in an order guaranteeing that every dependency is already computed. It has no recursion overhead and better locality, but it solves every subproblem whether needed or not.

Tabulation also permits space optimisation that memoisation cannot. If each row of the table depends only on the previous row, two rows suffice and the space drops from to .

3. The Classic Dynamic Programs

Each classic problem is defined by its state and its recurrence, and knowing those two is knowing the problem.

0/1 knapsack. State is (item index, remaining capacity). The recurrence chooses the better of taking the item and skipping it:

Cost is . This is pseudo-polynomial, not polynomial, because is a value rather than an input length — writing takes only bits, so the cost is exponential in the input size.

Longest common subsequence. State is (position in first string, position in second). Matching characters extend the subsequence; mismatches take the better of two skips. Cost is .

Edit distance. State is the same, with three operations — insert, delete, replace — each costing one. Cost is .

Matrix chain multiplication. State is (start, end) of a subchain, and the recurrence tries every split point. Cost is because there are states each taking to evaluate.

Longest increasing subsequence. The straightforward dynamic program is ; a patience-sorting formulation using binary search achieves .

Coin change. Counting the minimum coins needed is for denominations and amount , and it is a dynamic program precisely because greedy fails on general denominations.

4. Greedy Algorithms

A greedy algorithm makes the locally best choice at each step and never reconsiders.

It needs optimal substructure, like dynamic programming, plus the greedy choice property: a globally optimal solution can be reached by making the locally optimal choice.

That second condition is what must be proved, and it is what fails for most problems. The usual proof technique is an exchange argument: take any optimal solution, show it can be transformed into one containing the greedy choice without becoming worse, and conclude the greedy choice is safe.

Greedy is faster than dynamic programming when it works, because it considers one option per step rather than all of them. The risk is that it silently returns a wrong answer when the property does not hold.

ProblemGreedy works?Why
Fractional knapsackYesItems can be split, so ratio ordering is safe
0/1 knapsackNoAn item taken now may block a better pair later
Activity selectionYesEarliest finish leaves the most room
Coin change (arbitrary)NoA large coin may force many small ones
Coin change (canonical)YesThe denominations are designed for it
Minimum spanning treeYesThe cut property guarantees safety
Huffman codingYesTwo least frequent symbols are always deepest

The knapsack pair is the canonical contrast and appears constantly. Fractional knapsack is greedy by value-to-weight ratio and runs in ; 0/1 knapsack is a dynamic program at . The only difference is whether items can be split, and that single difference changes the technique entirely.

5. Why Greedy Fails on 0/1 Knapsack

The failure is worth seeing concretely, because it shows what the greedy choice property actually asserts.

Take a capacity of 10 and three items: A with value 60 and weight 10 (ratio 6), B with value 100 and weight 6 (ratio 16.7), and C with value 50 and weight 4 (ratio 12.5).

Greedy by ratio takes B first, using 6 of the capacity. Then C fits in the remaining 4, giving total value 150 and using the full capacity — which here happens to be optimal.

Change C's weight to 5. Greedy takes B (weight 6), then C no longer fits in the remaining 4, so it tries A, which also does not fit. Total value 100.

The optimal solution takes A alone, for value 60 — worse. So change A's value to 140: now greedy still takes B for 100, while taking A alone gives 140.

Greedy fails because taking the highest-ratio item consumed capacity that a lower-ratio but higher-value item needed. In the fractional version this cannot happen, since the leftover capacity is always filled by a fraction of the next item.

6. Classic Greedy Algorithms

Activity selection chooses the maximum number of non-overlapping activities. Sorting by earliest finishing time and taking greedily is optimal, and the exchange argument is short: the activity finishing earliest leaves the largest remaining interval, so swapping it into any optimal solution cannot reduce the count.

Sorting by earliest start or by shortest duration both fail, which is a standard multiple-choice distractor.

Huffman coding builds an optimal prefix-free code by repeatedly merging the two least frequent symbols. The greedy choice is safe because the two least frequent symbols must be siblings at the greatest depth in some optimal tree.

Job sequencing with deadlines schedules unit-time jobs to maximise profit, taking jobs in decreasing profit order and placing each as late as its deadline allows.

Minimum spanning tree algorithms are greedy in two different ways. Kruskal's adds the globally cheapest edge that does not form a cycle; Prim's grows a single tree by adding its cheapest outgoing edge. Both are justified by the cut property.

Dijkstra's algorithm is greedy over vertices, finalising the closest unfinalised vertex at each step. It requires non-negative weights, because a negative edge could reduce a distance already declared final.

7. Choosing Between the Three

The decision procedure is short.

First ask whether the subproblems overlap. If a naive recursion would recompute the same subproblem, dynamic programming is indicated. If not, divide and conquer suffices.

Then ask whether a locally optimal choice can be proved safe. If yes, greedy is faster and simpler. If the proof fails, or a counterexample exists, dynamic programming is required.

The cost ordering is usually greedy, then divide and conquer, then dynamic programming, and the applicability ordering is the reverse: dynamic programming works whenever greedy does, but not conversely.

A useful sanity check is to test a greedy idea on a small adversarial input before trusting it. Most greedy failures show up on three or four elements.

8. Worked Examples

Example 1. Solve the 0/1 knapsack problem with capacity 5 and items of (weight, value) .

Build the table where indexes items considered and is capacity.

With no items, every entry is 0.

Item 1, weight 2, value 3. For the value is 3; below that, 0. Row: 0, 0, 3, 3, 3, 3.

Item 2, weight 3, value 4. At , choose between skipping (3) and taking (), so 4. At , skip gives 3, take gives , so 4. At , skip gives 3, take gives , so 7. Row: 0, 0, 3, 4, 4, 7.

Item 3, weight 4, value 5. At , skip gives 4, take gives , so 5. At , skip gives 7, take gives , so 7. Row: 0, 0, 3, 4, 5, 7.

Item 4, weight 5, value 6. At , skip gives 7, take gives , so 7. Row: 0, 0, 3, 4, 5, 7.

The maximum value is 7, achieved by taking items 1 and 2 with total weight 5.

Note that the greedy ratio ordering would have picked item 1 (ratio 1.5) then item 2 (ratio 1.33) — which happens to give the same answer here. The table is what guarantees it.

Example 2. Solve the same instance as fractional knapsack.

Compute the value-to-weight ratios: , , , .

Sort descending and fill greedily.

Take item 1 entirely: weight 2, value 3. Remaining capacity 3.

Take item 2 entirely: weight 3, value 4. Remaining capacity 0.

Total value 7, the same as the 0/1 answer because the capacity happened to be filled exactly by whole items.

Change the capacity to 6 to see the difference. Fractional takes items 1 and 2 fully (weight 5, value 7) and then of item 3, adding for a total of 8.25. The 0/1 version cannot split item 3, so it must choose between items 1 and 2 (value 7) or items 2 and 3 (weight 7, too heavy) or items 1 and 3 (weight 6, value 8) — giving 8.

The fractional answer is always at least the 0/1 answer, and the gap is exactly what splitting buys.

Example 3. Find the length of the longest common subsequence of AGGTAB and GXTXAYB.

Build a table for prefixes of lengths and .

When characters match, . When they do not, .

Working through: the common characters that can be aligned in order are G, T, A, B.

Check that this is a genuine subsequence of both. In AGGTAB, the positions of G, T, A, B are 2, 4, 5, 6 — increasing. In GXTXAYB, they are 1, 3, 5, 7 — also increasing.

The length is 4.

The cost is table entries.

Why is this not greedy? A greedy match of the first common character would take G at position 2 of the first string and position 1 of the second, which happens to work here. But matching the first A of AGGTAB against the A of GXTXAYB would consume the A too early and lose the G, giving a shorter result. Only the table considers both branches.

Example 4. Six activities have (start, finish) times . Select the maximum number that do not overlap.

Sort by finishing time: .

Take , the earliest finisher. Current time 4.

starts at 3, before 4, so it conflicts. Skip.

starts at 0, conflicts. Skip.

starts at 5, after 4. Take it. Current time 7.

starts at 8, after 7. Take it. Current time 9.

starts at 5, conflicts. Skip.

Three activities are selected: .

Sorting by start time would have taken first, blocking both and and leaving only — two activities instead of three. Sorting by duration would have taken first at duration 1, then , then — three here, but that rule fails on other instances.

Only earliest-finish is provably optimal, and the reason is that it leaves the largest possible remaining interval at every step.

Example 5. Build a Huffman code for symbols with frequencies A:5, B:9, C:12, D:13, E:16, F:45. Give the total encoded length.

Repeatedly merge the two smallest frequencies.

Merge A(5) and B(9) into a node of 14. Remaining: 12, 13, 14, 16, 45.

Merge 12 and 13 into 25. Remaining: 14, 16, 25, 45.

Merge 14 and 16 into 30. Remaining: 25, 30, 45.

Merge 25 and 30 into 55. Remaining: 45, 55.

Merge 45 and 55 into 100, the root.

Now read the depths. F is a child of the root at depth 1. C and D are at depth 3, under the 25 node. E is at depth 3, under the 30 node. A and B are at depth 4, under the 14 node.

Total encoded bits: bits.

A fixed-length code would need 3 bits per symbol for 6 symbols, giving bits.

Huffman saves 76 bits, about 25 per cent. The saving comes from giving F, which occurs 45 times out of 100, a single bit rather than three.

Example 6. Why is 0/1 knapsack's cost called pseudo-polynomial?

A polynomial-time algorithm runs in time polynomial in the length of the input, measured in bits.

The input to knapsack is items and a capacity . Writing down items takes space proportional to , so a factor of in the running time is genuinely polynomial.

But writing down takes only bits, since it is a single number in binary. A capacity of one billion needs 30 bits.

So the running time is , which is exponential in the number of bits used to write .

Concretely, doubling the number of bits in squares the running time rather than doubling it. An instance with and requires table entries, which is intractable despite the input fitting on one line.

The term pseudo-polynomial names exactly this: polynomial in the numeric value of an input, exponential in its encoded length. The knapsack problem is NP-complete, and the existence of this algorithm does not contradict that, precisely because the algorithm is not polynomial in the input size.

Summary

Three techniques answer one question: how do the subproblems relate?

Independent subproblems mean divide and conquer, with cost given by a master-theorem recurrence. Strassen shows that reducing the branching factor from 8 to 7 is what lowers the exponent.

Overlapping subproblems plus optimal substructure mean dynamic programming. The gap between distinct subproblems and recursive calls is exactly what caching recovers.

Memoisation is top-down and solves only what is reached; tabulation is bottom-up, has better locality, and permits rolling-array space optimisation.

Each classic dynamic program is defined by its state and recurrence: knapsack on (item, capacity), LCS and edit distance on (position, position), matrix chain on (start, end), and coin change on amount.

Greedy needs optimal substructure plus a provable greedy choice property, established by an exchange argument. It is faster when it works and silently wrong when it does not.

Fractional knapsack is greedy by ratio; 0/1 knapsack is a dynamic program. The only difference is whether items can be split.

Activity selection is optimal by earliest finishing time, and both earliest start and shortest duration fail. Huffman merges the two least frequent symbols. Dijkstra requires non-negative weights.

is pseudo-polynomial because is a numeric value written in bits, so the cost is exponential in the input length.

Key formulas & results

Everything to memorise for the exam hall, in one card. Screenshot this for revision.

The organising tool
WHEN A PROBLEM BREAKS INTO SUBPROBLEMS, ASK HOW THEY RELATE. INDEPENDENT MEANS DIVIDE AND CONQUER. OVERLAPPING MEANS DYNAMIC PROGRAMMING. A PROVABLY SAFE CHOICE MEANS GREEDY.
TWO DIAGNOSTIC QUESTIONS SETTLE IT: DO THE SUBPROBLEMS OVERLAP, AND CAN A LOCAL CHOICE BE PROVED SAFE?
Divide and conquer
DIVIDE INTO SUBPROBLEMS, CONQUER RECURSIVELY, COMBINE. THE DEFINING PROPERTY IS THAT THE SUBPROBLEMS ARE DISJOINT, SO NO WORK IS DUPLICATED.
THE COST IS A RECURRENCE T(n) = a T(n/b) PLUS f(n), SOLVED BY THE MASTER THEOREM.
Standard divide-and-conquer costs
BINARY SEARCH IS T(n/2) PLUS O(1) GIVING log n. MERGE SORT IS 2T(n/2) PLUS O(n) GIVING n log n. STRASSEN IS 7T(n/2) PLUS O(n SQUARED) GIVING n TO THE 2.81.
NAIVE MATRIX MULTIPLICATION USES 8 RECURSIVE MULTIPLICATIONS GIVING EXPONENT 3. STRASSEN'S SAVING COMES ENTIRELY FROM REDUCING a FROM 8 TO 7.
The two dynamic programming conditions
OPTIMAL SUBSTRUCTURE MEANS AN OPTIMAL SOLUTION CONTAINS OPTIMAL SUBSOLUTIONS. OVERLAPPING SUBPROBLEMS MEANS THE SAME SUBPROBLEM IS SOLVED MANY TIMES BY NAIVE RECURSION.
BOTH ARE REQUIRED. OPTIMAL SUBSTRUCTURE ALONE PERMITS DIVIDE AND CONQUER; OVERLAP WITHOUT OPTIMAL SUBSTRUCTURE MEANS STORED ANSWERS ARE NOT REUSABLE.
What caching recovers
NAIVE FIBONACCI MAKES EXPONENTIALLY MANY CALLS BUT HAS ONLY n DISTINCT SUBPROBLEMS. STORING EACH RESULT MAKES IT LINEAR.
THE GAP BETWEEN THE NUMBER OF DISTINCT SUBPROBLEMS AND THE NUMBER OF RECURSIVE CALLS IS EXACTLY WHAT DYNAMIC PROGRAMMING RECOVERS.
Memoisation versus tabulation
MEMOISATION IS TOP-DOWN, CACHING RESULTS AS THE NATURAL RECURSION REACHES THEM. TABULATION IS BOTTOM-UP, FILLING A TABLE IN DEPENDENCY ORDER.
MEMOISATION SOLVES ONLY REACHABLE SUBPROBLEMS BUT PAYS RECURSION OVERHEAD. TABULATION SOLVES EVERY SUBPROBLEM BUT HAS BETTER LOCALITY AND NO RECURSION.
Rolling-array optimisation
IF EACH ROW OF THE TABLE DEPENDS ONLY ON THE PREVIOUS ROW, TWO ROWS SUFFICE AND THE SPACE DROPS FROM O(nW) TO O(W).
ONLY TABULATION PERMITS THIS, BECAUSE MEMOISATION CANNOT KNOW WHICH ENTRIES ARE STILL NEEDED. IT IS A STANDARD FOLLOW-UP QUESTION.
0/1 knapsack
V[i][w] = MAX OF V[i-1][w] AND v_i PLUS V[i-1][w MINUS w_i]. STATE IS ITEM INDEX AND REMAINING CAPACITY. COST IS O(nW).
THE TWO BRANCHES ARE SKIPPING THE ITEM AND TAKING IT, AND THE SECOND IS AVAILABLE ONLY WHEN THE ITEM FITS IN THE REMAINING CAPACITY.
The other classic programs
LCS AND EDIT DISTANCE HAVE STATE (POSITION, POSITION) AND COST O(mn). MATRIX CHAIN HAS STATE (START, END) AND COSTS O(n CUBED). COIN CHANGE COSTS O(nA).
MATRIX CHAIN IS CUBIC BECAUSE THERE ARE O(n SQUARED) STATES AND EACH TRIES O(n) SPLIT POINTS. LONGEST INCREASING SUBSEQUENCE IS O(n SQUARED) OR O(n log n).
The two greedy conditions
GREEDY NEEDS OPTIMAL SUBSTRUCTURE, LIKE DYNAMIC PROGRAMMING, PLUS THE GREEDY CHOICE PROPERTY: A GLOBAL OPTIMUM IS REACHABLE BY MAKING THE LOCALLY OPTIMAL CHOICE.
THE SECOND CONDITION IS WHAT MUST BE PROVED, AND IT IS WHAT FAILS FOR MOST PROBLEMS. GREEDY IS FASTER WHEN IT WORKS AND SILENTLY WRONG WHEN IT DOES NOT.
The exchange argument
TAKE ANY OPTIMAL SOLUTION, SHOW IT CAN BE TRANSFORMED INTO ONE CONTAINING THE GREEDY CHOICE WITHOUT BECOMING WORSE, AND CONCLUDE THE GREEDY CHOICE IS SAFE.
THIS IS THE STANDARD PROOF TECHNIQUE FOR THE GREEDY CHOICE PROPERTY, AND CONSTRUCTING IT IS WHAT DISTINGUISHES A JUSTIFIED GREEDY FROM A GUESS.
Where greedy works and fails
WORKS: FRACTIONAL KNAPSACK, ACTIVITY SELECTION, MINIMUM SPANNING TREE, HUFFMAN, CANONICAL COIN CHANGE. FAILS: 0/1 KNAPSACK, ARBITRARY COIN CHANGE.
THE KNAPSACK PAIR IS THE CANONICAL CONTRAST. THE ONLY DIFFERENCE IS WHETHER ITEMS CAN BE SPLIT, AND THAT SINGLE DIFFERENCE CHANGES THE TECHNIQUE ENTIRELY.
Why greedy fails on 0/1 knapsack
TAKING THE HIGHEST-RATIO ITEM CAN CONSUME CAPACITY THAT A LOWER-RATIO BUT HIGHER-VALUE ITEM NEEDED.
IN THE FRACTIONAL VERSION THIS CANNOT HAPPEN, BECAUSE LEFTOVER CAPACITY IS ALWAYS FILLED BY A FRACTION OF THE NEXT ITEM AND NOTHING IS WASTED.
Activity selection
SORT BY EARLIEST FINISHING TIME AND TAKE GREEDILY. THE ACTIVITY FINISHING EARLIEST LEAVES THE LARGEST REMAINING INTERVAL.
SORTING BY EARLIEST START OR BY SHORTEST DURATION BOTH FAIL, AND BOTH ARE STANDARD MULTIPLE-CHOICE DISTRACTORS.
Huffman coding
REPEATEDLY MERGE THE TWO LEAST FREQUENT SYMBOLS INTO A NEW NODE WHOSE FREQUENCY IS THEIR SUM, UNTIL ONE TREE REMAINS.
THE GREEDY CHOICE IS SAFE BECAUSE THE TWO LEAST FREQUENT SYMBOLS MUST BE SIBLINGS AT THE GREATEST DEPTH IN SOME OPTIMAL PREFIX-FREE TREE.
Greedy graph algorithms
KRUSKAL ADDS THE GLOBALLY CHEAPEST EDGE THAT FORMS NO CYCLE. PRIM GROWS ONE TREE BY ITS CHEAPEST OUTGOING EDGE. BOTH ARE JUSTIFIED BY THE CUT PROPERTY.
DIJKSTRA IS GREEDY OVER VERTICES AND REQUIRES NON-NEGATIVE WEIGHTS, BECAUSE A NEGATIVE EDGE COULD REDUCE A DISTANCE ALREADY DECLARED FINAL.
The decision procedure
FIRST ASK WHETHER SUBPROBLEMS OVERLAP; IF NOT, DIVIDE AND CONQUER SUFFICES. THEN ASK WHETHER A LOCAL CHOICE IS PROVABLY SAFE; IF SO, GREEDY IS FASTER.
DYNAMIC PROGRAMMING WORKS WHENEVER GREEDY DOES, BUT NOT CONVERSELY. TEST A GREEDY IDEA ON A SMALL ADVERSARIAL INPUT BEFORE TRUSTING IT.
Pseudo-polynomial
O(nW) IS POLYNOMIAL IN THE NUMERIC VALUE OF W BUT EXPONENTIAL IN ITS ENCODED LENGTH, SINCE WRITING W TAKES ONLY log W BITS.
DOUBLING THE BITS IN W SQUARES THE RUNNING TIME. THE KNAPSACK PROBLEM IS NP-COMPLETE AND THIS ALGORITHM DOES NOT CONTRADICT THAT.
⚠️

Traps GATE sets — and how to dodge them

These are the exact option-traps and misreads that cost marks under negative marking.

WATCH OUT
Applying greedy to 0/1 knapsack by value-to-weight ratio
Taking the highest-ratio item can consume capacity a higher-value item needed, and the wasted remainder cannot be filled by a fraction. Only the fractional version permits ratio ordering.
WATCH OUT
Assuming optimal substructure is enough for dynamic programming
Both conditions are required. Without overlapping subproblems there is nothing to cache, and divide and conquer is the correct and simpler technique.
WATCH OUT
Calling O(nW) knapsack a polynomial-time algorithm
W is a numeric value written in log W bits, so the cost is exponential in the input length. The correct term is pseudo-polynomial, and the distinction is why knapsack remains NP-complete.
WATCH OUT
Sorting activities by start time or duration
Only earliest finishing time is provably optimal, because it leaves the largest remaining interval. Earliest start can select one long activity that blocks several short ones.
WATCH OUT
Using Dijkstra's algorithm on a graph with negative edges
It finalises the closest unfinalised vertex at each step, and a negative edge encountered later could reduce a distance already declared final. Bellman-Ford handles negative weights.
WATCH OUT
Assuming greedy coin change always works
It works for canonical denominations designed for it, and fails otherwise. With coins 1, 3 and 4 making 6, greedy takes 4 then two 1s for three coins, while two 3s suffice.
WATCH OUT
Applying rolling-array optimisation to a memoised solution
Memoisation cannot know which cached entries are still needed, since the recursion reaches subproblems in an unpredictable order. Only tabulation's known fill order permits discarding old rows.
WATCH OUT
Treating memoisation and tabulation as always equivalent in cost
Memoisation solves only the subproblems actually reached, which can be far fewer, while tabulation fills the whole table. Tabulation wins on locality and recursion overhead when most states are needed.
WATCH OUT
Computing Strassen's exponent from the additions
The exponent is log base 2 of the number of recursive multiplications, which is 7 rather than 8. The extra additions increase the constant but do not change the exponent at all.
WATCH OUT
Asserting a greedy choice without an exchange argument
A greedy rule that works on the examples tried is not proved. The exchange argument transforms an arbitrary optimal solution to contain the greedy choice, and constructing it is what establishes correctness.
WATCH OUT
Confusing matrix chain multiplication with matrix multiplication
The dynamic program chooses the parenthesisation that minimises scalar multiplications; it does not perform the multiplication. Its O(n cubed) cost is over the chain length, not the matrix dimensions.
WATCH OUT
Assuming the fractional and 0/1 answers must differ
They coincide whenever the greedy fractional solution happens to use whole items. The fractional answer is always at least the 0/1 answer, and the gap is exactly what splitting buys.

Exam-pattern practice

PYQ-style questions with full solutions. Work through them as a readiness check — mark yourself honestly and get your gap report at the end.

Readiness check

Are you exam-ready for Algorithm Design: Greedy, Divide & Conquer, Dynamic Programming?

9 problems from this chapter. Try each one, reveal the worked solution, mark yourself honestly — get your gap report at the end.

9 questions~6 min

5-minute revision

The whole chapter, distilled. Read this the night before the exam.

  • Ask how the subproblems relate.
  • Independent means divide and conquer.
  • Overlapping means dynamic programming.
  • A provably safe choice means greedy.
  • Divide-and-conquer subproblems are disjoint.
  • Strassen reduces a from 8 to 7.
  • The exponent is log base 2 of a.
  • Extra additions change the constant, not the exponent.
  • DP needs optimal substructure and overlap together.
  • Caching recovers the gap between calls and distinct states.
  • Memoisation is top-down and solves only what is reached.
  • Tabulation is bottom-up with better locality.
  • Only tabulation permits rolling-array space saving.
  • Knapsack state is item index and remaining capacity.
  • Knapsack costs O(nW).
  • LCS and edit distance cost O(mn).
  • Matrix chain costs O(n cubed).
  • LIS is O(n squared) or O(n log n).
  • Greedy needs a provable greedy choice property.
  • Prove it with an exchange argument.
  • Fractional knapsack is greedy by ratio.
  • 0/1 knapsack is not.
  • The difference is whether items can be split.
  • Activity selection uses earliest finishing time.
  • Earliest start and shortest duration both fail.
  • Huffman merges the two least frequent symbols.
  • Kruskal and Prim are justified by the cut property.
  • Dijkstra requires non-negative weights.
  • Greedy coin change fails on arbitrary denominations.
  • Test a greedy idea on a small adversarial input.
  • DP works whenever greedy does, but not conversely.
  • O(nW) is pseudo-polynomial, not polynomial.
  • W is written in log W bits.
  • Knapsack remains NP-complete.

GATE question blueprint

How this topic is asked, tier by tier — so you can prep to the pattern.

Typical weightage: Algorithms contributes roughly 8-10 of the 72 core-CS marks; design techniques supply 3-4 of those across 2-3 questions

Question styleMarks eachTypical countWhat it tests
Technique selection1~1Matching a problem's subproblem structure to the right technique
Dynamic programming2~1Filling a knapsack or LCS table and reading off the answer
Dynamic programming costs1~1State counts and per-state work for the classic programs
Greedy failures2~1Constructing a counterexample and explaining the structural cause
Greedy proofs2~1Exchange arguments and why alternative orderings fail
Divide and conquer2~1Deriving exponents from the branching factor
Pseudo-polynomial2~1Why O(nW) is exponential in input length and what that implies
Technique comparison2~1Comparing two formulations of the same problem and what each gives up

Exam-hall strategy

Battle-tested tips from mentors and toppers for this topic under the sectional clock.

  1. Ask first whether subproblems overlap, then whether a choice is provably safe.
  2. Test any greedy rule on three or four adversarial elements before trusting it.
  3. For knapsack questions, check immediately whether items can be split.
  4. State the DP state and recurrence before filling any table.
  5. Compute Strassen-style exponents as log base b of a, ignoring the additive term.
  6. Call O(nW) pseudo-polynomial, never polynomial.
  7. Optimal values and table entries are commonly set as NAT, which carries no negative marking, so never leave one blank.
  8. For 1-mark and 2-mark MCQs, negative marking is -1/3 and -2/3, so guess only after eliminating an option.
  9. GATE gives a single freely-navigable 180-minute window, so flag a long DP table and return to it.

Beyond the exam

Where this skill shows up in the job you're competing for — and in life.

Diff between two files

The longest common subsequence dynamic program is what version-control tools compute to decide which lines changed between two revisions.

Compressing a file

Huffman coding is the greedy algorithm inside every general-purpose compressor, assigning short codes to frequent symbols and long codes to rare ones.

Laying out a network

Minimum spanning tree algorithms are greedy and are what determine the cheapest set of links connecting every site exactly once.

Scheduling a resource

Activity selection by earliest finishing time is the reasoning behind maximising how many bookings a single meeting room or machine can serve.

Where else this topic is tested

Prepare once, score in every exam that asks it.

GATE DAHigh overlap — dynamic programming and greedy reasoning appear, applied to sequence alignment and optimisation
UGC NET Computer ScienceHigh overlap — the classic dynamic programs and greedy algorithms are examined as direct recall of costs and conditions
ISRO / BARC / DRDO computer science papersVery high overlap — knapsack tables, Huffman codes and greedy counterexamples are recurring MCQ topics

Questions aspirants ask

Pulled from the Q&A community and mentor sessions.

Ask whether a locally optimal choice can be proved safe, and be genuinely sceptical about the answer. Both techniques require optimal substructure, so that condition does not distinguish them. What greedy additionally requires is the greedy choice property: that some globally optimal solution contains the choice the greedy rule makes at each step. That is a claim needing proof, usually by an exchange argument — take an arbitrary optimal solution, show it can be modified to include the greedy choice without becoming worse, and conclude the choice is safe. If the exchange argument goes through, greedy is correct and is faster, because it considers one option per step rather than all of them. If it does not, dynamic programming is required, and the failure usually reveals itself as a small counterexample. The practical routine under exam conditions is to try the greedy rule on three or four adversarial elements before trusting it. Most greedy failures appear at tiny sizes: coin change with denominations 1, 3, 4 fails at amount 6; 0/1 knapsack fails on three items; activity selection by shortest duration fails on three activities. If a small counterexample exists, it is nearly always easy to find. If several attempts fail to break the rule, that is weak evidence for correctness and the exchange argument should still be attempted before committing.

Because splitting removes the possibility of wasted capacity, which is exactly what breaks the greedy argument in the 0/1 case. In fractional knapsack, sort by value-to-weight ratio and fill from the top. When the next whole item does not fit, take the fraction of it that does, so the capacity is always used completely and every unit of capacity is filled with the highest-value material still available. An exchange argument formalises this: if an optimal solution uses any lower-ratio material while higher-ratio material remains unused, swapping a small quantity strictly increases the value, so the optimum must be the ratio-greedy filling. In 0/1 knapsack the swap is unavailable. Taking a high-ratio item consumes capacity in an indivisible chunk, and the remainder may be too small for anything useful. Concretely, with capacity 8 and items of weight 3 value 30, weight 4 value 50 and weight 5 value 60, the ratios are 10, 12.5 and 12. Greedy takes the weight-4 item then the weight-3 item, using 7 of 8 and reaching value 80. The optimum takes the weight-3 and weight-5 items, using all 8 for value 90. The single unusable unit of capacity is what greedy could not recover, and no ordering fixes it. The dynamic program considers both branches at every item, which is why it costs O(nW) rather than O(n log n) and why that cost is unavoidable here.

Use memoisation when the reachable state space is much smaller than the full table, and tabulation when most states are needed or when space must be reduced. Memoisation writes the natural recursion and caches results, so it solves exactly the subproblems the recursion actually reaches. For a problem where the recursion touches only a sparse subset of the state space — a knapsack where the weights are large and few capacity values are ever queried, for instance — this can be dramatically cheaper than filling the whole table. It is also easier to write correctly, since the recurrence is transcribed directly with no need to work out a valid fill order. Tabulation fills the table bottom-up in an order guaranteeing every dependency is already present. It pays no recursion overhead, has sequential memory access and therefore better cache behaviour, and cannot overflow the stack on deep recursions. Its distinctive advantage is space: if each row depends only on the previous row, two rows suffice and the space drops from O(nW) to O(W). Memoisation cannot do this, because it has no way to know which cached entries are still needed. The costs are that it solves every state whether reachable or not, and that the fill order must be derived, which is where errors enter. In an exam, tabulation is usually what a question about space optimisation is asking for, and memoisation is usually what a question about avoiding redundant computation is asking for.

Polynomial in the numeric value of an input, but exponential in the number of bits used to write it. Complexity theory measures input size in bits, because that is the only measure that behaves consistently across encodings. A knapsack instance consists of n items and a capacity W. The n items take space proportional to n, so a factor of n in the running time is polynomial in the input length. But W is a single number, and writing it in binary takes only log base 2 of W bits — a capacity of one billion occupies 30 bits. The O(nW) running time is therefore O(n times 2 to the power of the number of bits in W), which is exponential in the input length. The practical consequence is severe. An instance with 50 items and a capacity of 2 to the 40 fits comfortably on one line of text, yet the table would need about 5 times 10 to the 13 entries. Adding a single bit to the capacity doubles the work. The theoretical consequence is that this algorithm does not place knapsack in P, and knapsack remains NP-complete. What it does establish is that knapsack is not strongly NP-complete: instances whose numbers are bounded by a polynomial in n are solvable in genuinely polynomial time. That distinction matters, because problems such as 3-SAT remain hard even when every number in the input is small, and no pseudo-polynomial algorithm can exist for them unless P equals NP.

It is greedy because at each step it permanently finalises the unfinalised vertex with the smallest tentative distance, and never reconsiders that decision. The safety argument is an exchange argument in disguise: if the closest unfinalised vertex is v at tentative distance d, then any path to v through another unfinalised vertex must first reach that vertex, which is at distance at least d, and then continue along edges of non-negative weight — so it cannot arrive at v with total cost below d. The tentative distance is therefore already final, and finalising it is safe. Notice exactly where non-negativity entered. The argument required that continuing from a vertex at distance at least d could not reduce the total below d, which is true only if every subsequent edge has non-negative weight. Introduce a negative edge and the reasoning collapses. Concretely, take a source S with an edge of weight 2 to A and an edge of weight 5 to B, plus an edge of weight minus 4 from B to A. Dijkstra finalises A at distance 2, since 2 is smaller than 5, and never revisits it. But the genuine shortest path to A goes S to B to A for a total of 1. The algorithm reports 2, which is simply wrong. The remedy is Bellman-Ford, which abandons the greedy finalisation and instead relaxes every edge V minus 1 times, at cost O(VE) rather than O(E log V). It also detects negative cycles, which Dijkstra cannot, and which make shortest paths undefined.
Header Logo