Skip to content
Chapter 5Basic techniques· ~75 min· Book pages 47–56Book ke pages 47–56

Complete search

Generate every candidate, then prune the search until it is fast enough.Saare possible answers generate karo, phir pruning se search ko fast banao.

What you will learnIs chapter mein kya seekhoge

  • Generate every subset and every permutation — recursively, with bitmasks, and with nextPermutation.Har subset aur har permutation generate karna — recursion se, bitmasks se, aur nextPermutation se.
  • Backtracking — build a solution step by step and undo when it can't work (n queens).Backtracking — solution step by step banao aur kaam na bane toh undo karo (n queens).
  • Pruning — cut hopeless branches early; one example goes from 483 s to 0.6 s.Pruning — bekaar branches jaldi kaato; ek example 483 s se 0.6 s tak aata hai.
  • Meet in the middle — turn 2ⁿ work into about 2^(n/2).Meet in the middle — 2ⁿ ka kaam lagbhag 2^(n/2) mein badalna.

Helps to know: Pehle se pata ho toh achha: 2. Time complexity, 3. Sorting

Complete search means trying every possible solution and keeping the best one, or counting them. It works for almost any problem, it’s easy to write, and it’s always correct. The only question is whether there’s time. Use the input limits from chapter 2: n ≤ 20 hints at 2ⁿ subsets and n ≤ 10 at n! permutations. When it’s too slow, the rest of the book (greedy, dynamic programming, …) takes over.

Complete search ka matlab: har possible solution try karo, phir best rakho ya sabko gino. Yeh lagbhag har problem pe chalta hai, likhna aasaan hai, aur hamesha sahi answer deta hai — bas sawaal yeh hai ki time hai ya nahi. Chapter 2 ki input limits dekho: n ≤ 20 matlab shayad 2ⁿ subsets, n ≤ 10 matlab n! permutations. Jab yeh slow pade, tab book ka baaki hissa (greedy, dynamic programming, …) kaam aata hai.

5.1Generating subsets

Method 1, recursion: search(k) decides element k. First it leaves k out, then takes it, recursing on k + 1 each time, and k == n means one complete subset. The calls form a binary tree with 2ⁿ leaves, one per subset.

Method 2, bitmasks: each subset of {0, …, n−1} is an n-bit number b. Bit i is 1 exactly when element i is in the subset. Looping b from 0 to 2ⁿ − 1 visits every subset. For example, 25 = 11001₂ means {0, 3, 4}.

Method 1, recursion: search(k) element k ka faisla karta hai — pehle k ko chhodta hai, phir leta hai, aur dono baar k + 1 pe recurse karta hai. k == n matlab ek poora subset ban gaya. Calls ek binary tree banate hain jiske 2ⁿ leaves hain — har subset ke liye ek.

Method 2, bitmasks: {0, …, n−1} ka har subset ek n-bit number b hai: bit i = 1 tabhi jab element i subset mein hai. b ko 0 se 2ⁿ − 1 tak chalao, har subset mil jayega. Jaise 25 = 11001₂ matlab {0, 3, 4}.

JAVAch05/Generate.java
static List<Integer> subset = new ArrayList<>(); /** Decide element k: leave it out, then take it. */static void search(int k) {    if (k == n) {        out.add(new ArrayList<>(subset)); // process subset    } else {        search(k + 1);        subset.add(k);        search(k + 1);        subset.remove(subset.size() - 1);    }}
JAVAch05/Generate.java
/** Subsets as the bits of b = 0 .. 2^n - 1: bit i set ⇔ element i is in the subset. */static List<List<Integer>> subsetsByBits(int n) {    List<List<Integer>> all = new ArrayList<>();    for (int b = 0; b < (1 << n); b++) {        List<Integer> s = new ArrayList<>();        for (int i = 0; i < n; i++) {            if ((b & (1 << i)) != 0) s.add(i);        }        all.add(s);    }    return all;}
VisualiserGenerating subsets and permutations
Generate
  • current
  • path to root
  • used in answer
k=0
current subset
found so far (0)
1/30

search(0): first leave element 0 OUT (left branch).

static List<Integer> subset = new ArrayList<>(); /** Decide element k: leave it out, then take it. */static void search(int k) {    if (k == n) {        out.add(new ArrayList<>(subset)); // process subset    } else {        search(k + 1);        subset.add(k);        search(k + 1);        subset.remove(subset.size() - 1);    }}
k
0
My notesMere notes

5.2Generating permutations

Method 1, recursion: fill the permutation one position at a time. At each step, try every element not yet chosen, recurse, then undo. This visits all n! permutations in lexicographic order.

Method 2, next permutation: start from the sorted order and repeatedly step to the next permutation. C++ has next_permutation, but Java doesn’t, so here it is:

  1. Find the longest non-increasing suffix.
  2. Swap the element just before it with the rightmost larger element.
  3. Reverse the suffix.

It also handles duplicates correctly, generating each distinct arrangement once.

Method 1, recursion: permutation ek-ek position bharo. Har step pe har woh element try karo jo abhi chosen nahi hai, recurse karo, phir undo. Saare n! permutations lexicographic order mein milte hain.

Method 2, next permutation: sorted order se shuru karo aur baar baar agle permutation pe jao. C++ mein next_permutation hai, Java mein nahi — toh yeh raha:

  1. Sabse lamba non-increasing suffix dhundho.
  2. Usse theek pehle wale element ko suffix ke sabse right wale bade element se swap karo.
  3. Suffix ulta kar do.

Duplicates ko bhi sahi sambhalta hai — har distinct arrangement ek hi baar.

JAVAch05/Generate.java
static List<Integer> perm = new ArrayList<>();static boolean[] chosen; static void permute() {    if (perm.size() == n) {        out.add(new ArrayList<>(perm)); // process permutation        return;    }    for (int i = 0; i < n; i++) {        if (chosen[i]) continue;        chosen[i] = true;        perm.add(i);        permute();        chosen[i] = false;        perm.remove(perm.size() - 1);    }}
JAVAch05/Generate.java
/** C++'s next_permutation, which Java lacks. Returns false after the last (descending) permutation. */static boolean nextPermutation(int[] a) {    int i = a.length - 2;    while (i >= 0 && a[i] >= a[i + 1]) i--; // longest non-increasing suffix    if (i < 0) return false;    int j = a.length - 1;    while (a[j] <= a[i]) j--; // rightmost element bigger than a[i]    int t = a[i]; a[i] = a[j]; a[j] = t;    for (int l = i + 1, r = a.length - 1; l < r; l++, r--) { t = a[l]; a[l] = a[r]; a[r] = t; }    return true;}
My notesMere notes

5.3Backtracking

Backtracking builds a solution step by step and abandons a partial solution as soon as it breaks a rule. Then it undoes the last step and tries the next option.

n queens: count the ways to place n queens on an n × n board so that no two attack each other. Place one queen per row. Three boolean arrays remember which columns and diagonals are taken:

  • column[x] for each column;
  • diag1[x + y], because x + y is constant along one diagonal direction;
  • diag2[x − y + n − 1], the same for the other direction.

That way each check is O(1). The answers grow fast: q(4) = 2, q(8) = 92, and q(16) = 14,772,512 already takes about a minute.

Backtracking solution ko step by step banata hai, aur jaise hi koi partial solution rule todta hai, use chhod deta hai — aakhri step undo karke agla option try karta hai.

n queens: n × n board pe n queens aise rakho ki koi kisi pe attack na kare — kitne tareeke? Har row mein ek queen. Teen boolean arrays yaad rakhte hain kaunse columns aur diagonals bhare hain:

  • har column ke liye column[x];
  • diag1[x + y] — ek diagonal direction mein x + y constant rehta hai;
  • diag2[x − y + n − 1] — doosri direction ke liye wahi.

Isliye har check O(1). Answers tezi se badhte hain: q(4) = 2, q(8) = 92, aur q(16) = 14,772,512 mein lagbhag ek minute lag jaata hai.

JAVAch05/NQueens.java
static int n, count;static boolean[] column, diag1, diag2; // diag1: x + y, diag2: x - y + n - 1 static void search(int y) {    if (y == n) {        count++;        return;    }    for (int x = 0; x < n; x++) {        if (column[x] || diag1[x + y] || diag2[x - y + n - 1]) continue;        column[x] = diag1[x + y] = diag2[x - y + n - 1] = true;        search(y + 1);        column[x] = diag1[x + y] = diag2[x - y + n - 1] = false;    }} static int queens(int size) {    n = size;    count = 0;    column = new boolean[n];    diag1 = new boolean[2 * n - 1];    diag2 = new boolean[2 * n - 1];    search(0);    return count;}
VisualiserBacktracking: n queens
  • queen
  • safe: placing
  • attacked: skip
  • attacked square
  • just removed
x0x1x2x3y0y1y2y3
1/80

Place one queen per row, top to bottom. Grey squares are attacked by the queens already placed.

static int n, count;static boolean[] column, diag1, diag2; // diag1: x + y, diag2: x - y + n - 1 static void search(int y) {    if (y == n) {        count++;        return;    }    for (int x = 0; x < n; x++) {        if (column[x] || diag1[x + y] || diag2[x - y + n - 1]) continue;        column[x] = diag1[x + y] = diag2[x - y + n - 1] = true;        search(y + 1);        column[x] = diag1[x + y] = diag2[x - y + n - 1] = false;    }} static int queens(int size) {    n = size;    count = 0;    column = new boolean[n];    diag1 = new boolean[2 * n - 1];    diag2 = new boolean[2 * n - 1];    search(0);    return count;}
My notesMere notes

5.4Pruning the search

Pruning adds intelligence: notice as early as possible that a partial solution can’t be completed, and stop exploring it.

The book’s example counts paths through an n × n grid, from the upper-left to the lower-right corner, that visit every square exactly once. A 7 × 7 grid has 111,712 of them. Four simple observations take the search from 483 s to 0.6 s:

  1. Symmetry: the first move is down or right, and the two cases mirror each other, so explore only “down” and double the count.
  2. Too early: reaching the corner before visiting everything ends the path, because it can’t be completed.
  3. Wall split: if a wall blocks the way forward but both left and right are free, the unvisited squares are cut into two parts. Only one can be reached, so stop.
  4. Any split: the same when the path itself (not just a wall) blocks the way ahead.

Pruning dimaag lagata hai: jitni jaldi ho sake pehchano ki ek partial solution poora nahi ho sakta, aur use explore karna band karo.

Book ka example n × n grid mein upper-left se lower-right corner tak aise paths ginta hai jo har square theek ek baar visit karein — 7 × 7 mein aise 111,712 hain. Chaar simple observations search ko 483 s se 0.6 s tak le aate hain:

  1. Symmetry: pehla move neeche ya right — dono ek-doosre ka mirror hain, toh sirf “neeche” explore karo aur count double kar do.
  2. Bahut jaldi: saare squares visit karne se pehle corner pe pahunche toh path wahin khatam — poora ho hi nahi sakta.
  3. Wall split: aage wall hai par left aur right dono free hain — bache hue squares do hisson mein kat gaye. Ek hi tak pahunch sakte ho, toh ruko.
  4. Koi bhi split: wahi baat jab aage ka raasta path khud (sirf wall nahi) rok raha ho.
JAVAch05/GridPaths.java
static int n, level;static boolean[][] seen;static long paths, calls;static final int[] DR = {1, 0, -1, 0}, DC = {0, 1, 0, -1}; // down, right, up, left static boolean free(int r, int c) {    return r >= 0 && c >= 0 && r < n && c < n && !seen[r][c];} static void search(int r, int c, int visited, int dir) {    calls++;    if (r == n - 1 && c == n - 1) {        if (visited == n * n) {            paths++;            return;        }        if (level >= 2) return;    }    if (level >= 3 && dir >= 0) {        int ar = r + DR[dir], ac = c + DC[dir];        boolean wallAhead = ar < 0 || ac < 0 || ar >= n || ac >= n;        // Optimisation 3: a wall ahead; optimisation 4: anything blocking ahead (wall or the path).        if (level >= 4 ? !free(ar, ac) : wallAhead) {            int lf = (dir + 1) % 4, rt = (dir + 3) % 4;            // Both sides are free, so the unvisited squares are split in two: give up.            if (free(r + DR[lf], c + DC[lf]) && free(r + DR[rt], c + DC[rt])) return;        }    }    for (int d = 0; d < 4; d++) {        int nr = r + DR[d], nc = c + DC[d];        if (!free(nr, nc)) continue;        seen[nr][nc] = true;        search(nr, nc, visited + 1, d);        seen[nr][nc] = false;    }} static long count(int size, int optimisations) {    n = size;    level = optimisations;    paths = calls = 0;    seen = new boolean[n][n];    seen[0][0] = true;    if (n == 1) return 1;    if (level >= 1) {        // Optimisation 1: by symmetry, always step down first and double the answer.        seen[1][0] = true;        search(1, 0, 2, 0);        return 2 * paths;    }    search(0, 0, 1, -1);    return paths;}
VisualiserPruning lab: grid paths

Count paths from the top-left to the bottom-right square that visit every square once. Each level adds one of the book’s optimisations; all levels must find the same number of paths.

LevelPathsCalls
0 Basic backtracking——
1 + symmetry: first step down only, ×2——
2 + stop on reaching the corner too early——
3 + wall ahead, both sides free → split——
4 + anything ahead, both sides free → split——

The book’s 7×7 measurements (111,712 paths)

0 Basic backtracking483 s7.6·10¹⁰ calls
1 + symmetry: first step down only, ×2244 s3.8·10¹⁰ calls
2 + stop on reaching the corner too early119 s2.0·10¹⁰ calls
3 + wall ahead, both sides free → split1.8 s2.2·10⁸ calls
4 + anything ahead, both sides free → split0.6 s6.9·10⁷ calls

From 483 s to 0.6 s, about 800× faster, with the same answer. The biggest wins prune near the top of the search tree.

My notesMere notes

5.5Meet in the middle

Meet in the middle splits the search space into two halves, searches each half separately, then combines the results efficiently. It typically turns 2ⁿ into about 2^(n/2): for n = 40, that’s 10⁶ work per half instead of 10¹².

Subset sum: can some numbers of the list add up to x?

  1. Split the list into A and B.
  2. List all subset sums S_A and S_B, sorted.
  3. Ask whether some s_a + s_b = x. Two pointers do it in linear time: start with the smallest of S_A and the largest of S_B, and move whichever side brings the sum closer to x.

For [2, 4, 5, 9] and x = 15: S_A = [0, 2, 4, 6] and S_B = [0, 5, 9, 14], and 6 + 9 = 15, which is the subset {2, 4, 9}.

Meet in the middle search space ko do hisson mein baant-ta hai, dono ko alag search karta hai, phir results ko efficiently jodta hai. Aam taur pe 2ⁿ ko lagbhag 2^(n/2) bana deta hai — n = 40 pe 10¹² ki jagah har half pe 10⁶.

Subset sum: kya list ke kuch numbers ka sum x ban sakta hai?

  1. List ko A aur B mein todo.
  2. Saare subset sums S_A aur S_B nikaalo, sorted.
  3. Poocho: kya koi s_a + s_b = x? Two pointers isse linear time mein karte hain: S_A ke sabse chhote aur S_B ke sabse bade se shuru karo, aur jo side sum ko x ke paas laaye use khiskao.

[2, 4, 5, 9] aur x = 15 ke liye: S_A = [0, 2, 4, 6], S_B = [0, 5, 9, 14], aur 6 + 9 = 15 — yaani subset {2, 4, 9}.

JAVAch05/MeetInTheMiddle.java
/** All subset sums of a[from..to), sorted. */static long[] subsetSums(long[] a, int from, int to) {    int m = to - from;    long[] s = new long[1 << m];    for (int b = 0; b < (1 << m); b++) {        for (int i = 0; i < m; i++) if ((b & (1 << i)) != 0) s[b] += a[from + i];    }    Arrays.sort(s);    return s;}
JAVAch05/MeetInTheMiddle.java
static boolean canMake(long[] a, long x) {    int half = a.length / 2;    long[] sa = subsetSums(a, 0, half);    long[] sb = subsetSums(a, half, a.length);    int i = 0, j = sb.length - 1; // smallest of SA with the largest of SB    while (i < sa.length && j >= 0) {        long s = sa[i] + sb[j];        if (s == x) return true;        if (s < x) i++;        else j--;    }    return false;}
VisualiserMeet in the middle: subset sum
  • current
  • being read
  • used in answer
the list, split in half
01232459AB
1/8

Can some numbers add up to 15? Trying all 2^4 = 16 subsets works, but split the list in half instead: A and B.

static boolean canMake(long[] a, long x) {    int half = a.length / 2;    long[] sa = subsetSums(a, 0, half);    long[] sb = subsetSums(a, half, a.length);    int i = 0, j = sb.length - 1; // smallest of SA with the largest of SB    while (i < sa.length && j >= 0) {        long s = sa[i] + sb[j];        if (s == x) return true;        if (s < x) i++;        else j--;    }    return false;}
My notesMere notes

Cheat sheet

The whole chapter on one screen — revise from here before a contest.Poora chapter ek screen pe — contest se pehle yahin se revise karo.

Subsets

Recursion: skip k, take k, undo. Bits: for (b = 0; b < 1 << n; b++), and element i is in when (b & (1 << i)) != 0. Both are O(2ⁿ · n).

Recursion: k chhodo, k lo, undo. Bits: for (b = 0; b < 1 << n; b++), element i andar jab (b & (1 << i)) != 0. Dono O(2ⁿ · n).

Permutations

Recursion with chosen[], or nextPermutation: non-increasing suffix → swap → reverse. n ≤ 10 is fine.

chosen[] ke saath recursion, ya nextPermutation: non-increasing suffix → swap → reverse. n ≤ 10 theek.

Backtracking

Extend, recurse, undo. Make the "is this allowed?" check O(1) with helper arrays (queens: column, x+y, x−y+n−1).

Badhao, recurse, undo. "Allowed hai?" check helper arrays se O(1) (queens: column, x+y, x−y+n−1).

Pruning

Stop as soon as a partial solution can’t finish. Early cuts beat late cuts. Use symmetry to halve work.

Jaise hi partial solution poora na ho sake, ruko. Jaldi ke cuts der ke cuts se behtar. Symmetry se kaam aadha.

Meet in the middle

Split in two, enumerate each half (2^(n/2)), sort, combine with two pointers. Use it for n ≈ 40 subset problems.

Do mein todo, har half enumerate (2^(n/2)), sort, two pointers se jodo. n ≈ 40 wale subset problems ke liye.

6 left

Check yourselfKhud ko check karo

Pick an answer to see the explanation. Your best score is saved.Answer chuno, explanation turant dikhega. Tumhara best score save hota hai.

Q1trace it

Which subset of {0, 1, 2, 3, 4} does the bitmask 25 represent?

Q2complexity

How many calls does the recursive subset search make for n = 3?

Q3trace it

What is nextPermutation of [1, 3, 2]?

Q4concept

In n queens, which diag1 index do squares (x=1, y=2) and (x=2, y=1) share?

Q5complexity

Trying all permutations is comfortable in 1 second up to about…

Q6concept

Why must backtracking undo its change after the recursive call returns?

Q7concept

Which pruning helps the most?

Q8complexity

Subset sum with n = 40 numbers. Roughly how many subset sums does meet in the middle generate?

Answered 0 of 8.

Practice on CSESCSES pe practice karo

The CSES Problem Set is the book’s companion. Solve these in Java, roughly in this order, and track them here.CSES Problem Set is book ka saathi hai. Inhe Java mein solve karo, lagbhag isi order mein, aur yahin track karo.

  1. easyApple Division§5.1 n ≤ 20: try all 2ⁿ splits with a bitmask.n ≤ 20: bitmask se saare 2ⁿ splits try karo.
    My Java solution

  2. easyCreating Strings§5.2 Sort the characters, then nextPermutation handles duplicates.Characters sort karo, phir nextPermutation duplicates sambhal leta hai.
    My Java solution

  3. mediumGray Code§5.1 Neighbouring masks differ in one bit: i ^ (i >> 1).Padosi masks mein ek bit ka fark: i ^ (i >> 1).
    My Java solution

  4. mediumChessboard and Queens§5.3 The n-queens backtracking, plus reserved squares.n-queens backtracking + reserved squares.
    My Java solution

  5. hardGrid Path Description§5.4 Exactly the pruning ideas from 5.4 (7×7, ending bottom-left).Bilkul 5.4 wale pruning ideas (7×7, bottom-left pe end).
    My Java solution

  6. hardMeet in the Middle§5.5 Count pairs of sums with sorted arrays and two pointers. Avoid HashMap.Sorted arrays + two pointers se sums ke pairs gino — HashMap se bacho.
    My Java solution