Backtracking & Branch-and-Bound

Backtracking ও Branch and Bound

Read: ~40 min Advanced 7 practice problems Live code runner

1. Backtracking = DFS + Pruning

Backtracking explores a tree of partial solutions. The template:

  1. Choose the next decision.
  2. Explore (recurse).
  3. Un-choose (backtrack to previous state).

Without pruning, backtracking is naive brute force and explodes exponentially. With pruning, many practical NP-hard problems become solvable for moderate sizes.

Backtracking আসলে DFS-এর একটি বিশেষ রূপ — প্রতিটি branch-এ "choose / explore / un-choose"। সঠিক pruning ছাড়া এটি ব্যবহার অর্থহীন; সঠিক pruning সহ এটি Sudoku, SAT, N-queens সমাধান করে।

2. N-Queens — The Classic

Place N queens on N × N so none attack each other. Place row by row; for each row, try each column — prune if column / diagonal already used.

nqueens.cpp
#include <bits/stdc++.h>
using namespace std;

int N = 8, count_ = 0;
vector<bool> col, d1, d2;

void solve(int r) {
    if (r == N) { count_++; return; }
    for (int c = 0; c < N; c++) {
        if (col[c] || d1[r + c] || d2[r - c + N]) continue;
        col[c] = d1[r + c] = d2[r - c + N] = true;
        solve(r + 1);
        col[c] = d1[r + c] = d2[r - c + N] = false;
    }
}

int main() {
    col.assign(N, false);
    d1.assign(2*N, false);
    d2.assign(2*N, false);
    solve(0);
    cout << "N=" << N << " solutions = " << count_;   // 92 for N=8
}

3. Generate All Permutations

perms.cpp
#include <bits/stdc++.h>
using namespace std;

vector<int> cur;
vector<bool> used;

void go(vector<int>& a) {
    if (cur.size() == a.size()) {
        for (int x : cur) cout << x << " ";
        cout << "\n"; return;
    }
    for (int i = 0; i < (int)a.size(); i++) {
        if (used[i]) continue;
        used[i] = true; cur.push_back(a[i]);
        go(a);
        cur.pop_back(); used[i] = false;
    }
}

int main() {
    vector<int> a = {1, 2, 3};
    used.assign(a.size(), false);
    go(a);
}

4. Branch and Bound — Pruning by Bounds

Branch-and-bound adds a numeric bound on the best achievable from a partial state. If the bound can't beat the current best, prune the entire branch. Common in 0/1 knapsack, TSP, integer programming.

Knapsack BB At each node, sort remaining items by value-to-weight ratio. Compute an LP relaxation upper bound (allow fractional last item). If < current best, prune.

5. Iterative Deepening DFS

DFS with a depth limit, increasing the limit by 1 until a solution is found. Combines DFS's low memory with BFS's optimal-depth guarantee. Used in IDA* for puzzles, in iterative-deepening alpha-beta for chess engines.

DFS-এর memory + BFS-এর optimal — দুটোই পেতে হলে iterative deepening। প্রতিটি depth নতুন করে চালাতে হয়, কিন্তু খরচ আগের সব চালানোর সমষ্টির ~(branching) গুণ — সাধারণত গ্রহণযোগ্য।

6. Practice Problems

  1. Generate all subsets (power set) of {1, 2, 3}.
    Power set।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: for each index i, recurse twice — once with a[i] included, once without. 2ⁿ leaves.

  2. Combination sum — find all combinations summing to target (each number can be reused).
    Combination sum (reuse allowed)।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: sort. DFS with starting index. From current index, either include current (recurse with same start) or skip to next (start = i+1).

  3. Word break II — return all valid sentence segmentations of a string given a dictionary.
    Word break II।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: backtracking + DP cache. For each starting index, try every split point; if prefix is in dictionary, recurse on suffix.

  4. Restore IP addresses — split a digit string into 4 parts each in 0..255.
    IP address restore।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: recurse with parts placed so far; try lengths 1, 2, 3 for each part; prune leading zeros and out-of-range.

  5. Rat in a maze — print all paths from (0,0) to (n−1,n−1).
    Rat in a maze।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: DFS with visited matrix. Move U/D/L/R; on each, mark visited, recurse, then un-mark.

  6. Palindrome partitioning — list every way to split a string into palindromic pieces.
    Palindrome partitioning।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: at each starting index, try every prefix; if it is a palindrome, recurse on the rest.

  7. Sudoku solver — backtracking with row/col/box constraints.
    Sudoku solver।
    ✨ Show Answer (উত্তর দেখুন)
    a7.cpp
    #include <bits/stdc++.h>
    using namespace std;
    int g[9][9];
    bool r[9][10], c[9][10], b[9][10];
    bool solve(int i, int j) {
        if (i == 9) return true;
        int ni = j == 8 ? i+1 : i, nj = (j+1)%9;
        if (g[i][j]) return solve(ni, nj);
        int bi = (i/3)*3 + j/3;
        for (int v = 1; v <= 9; v++)
            if (!r[i][v] && !c[j][v] && !b[bi][v]) {
                g[i][j] = v; r[i][v] = c[j][v] = b[bi][v] = true;
                if (solve(ni, nj)) return true;
                g[i][j] = 0; r[i][v] = c[j][v] = b[bi][v] = false;
            }
        return false;
    }
    int main() {
        // fill g[][] with puzzle (0 = blank), set r/c/b, then solve(0,0).
        cout << "sudoku solver template";
    }

Summary — Module 37

Backtracking is DFS with pruning — the choose/explore/un-choose pattern. Branch and bound adds numeric pruning bounds. Iterative deepening combines DFS memory with BFS optimality. Sudoku, N-queens, permutations, and many "list every X" problems all use this template.

"List every solution" দেখলেই backtracking। সঠিক pruning ছাড়া exponential — সঠিক pruning সহ NP-hard সমস্যাও practical।

Next Module → String Algorithms: KMP, Z, Suffix Array।