State Space Search / Exhaustive Search

Concept

Exhaustive search (brute force with backtracking) systematically explores all possible states. Generate all permutations, subsets, or combinations and check each one. Backtracking prunes branches early when a partial solution can't lead to a valid answer, making it faster than pure brute force.

When to Use

  • Generating all permutations of n elements (n <= 10)
  • Generating all subsets of a set (n <= 20)
  • N-Queens, Sudoku solver, maze solving
  • Any problem with small constraints where you must try all possibilities
  • When no polynomial-time algorithm exists (NP-hard problems with small n)

Intuition

Think of it as exploring a decision tree. At each step you make a choice (pick an element, place a queen, etc.), go deeper, then undo the choice and try the next option. It's like trying every combination on a lock, but smarter — if the first two digits are already wrong, skip all combinations starting with them. The 'undo' step (backtrack) is what makes it memory-efficient compared to generating everything upfront.

Complexity

Time
O(n!) for permutations, O(2^n) for subsets
Space
O(n) recursion stack

Template Code

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

// --- Generate All Permutations ---
void permute(vector<int> &v, int idx){
    if(idx == v.size()){
        for(int x : v) cout << x << " ";
        cout << "\n";
        return;
    }

    for(int i = idx; i < v.size(); i++){
        swap(v[idx], v[i]);
        permute(v, idx+1);
        swap(v[idx], v[i]);  // backtrack
    }
}

// --- Generate All Subsets (Bitmask) ---
void subsets(vector<int> &v){
    int n = v.size();
    for(int mask = 0; mask < (1 << n); mask++){
        cout << "{ ";
        for(int i = 0; i < n; i++){
            if(mask & (1 << i)) cout << v[i] << " ";
        }
        cout << "}\n";
    }
}

// --- Backtracking Template ---
int n, ans = 0;
vector<bool> visited;

void solve(int depth){
    if(depth == n){
        ans++;
        return;
    }

    for(int i = 0; i < n; i++){
        if(visited[i]) continue;
        // add pruning conditions here

        visited[i] = true;
        solve(depth+1);
        visited[i] = false;  // backtrack
    }
}

int main(){
    ios_base::sync_with_stdio(0), cin.tie(0);

    cin >> n;

    // Permutations
    vector<int> v(n);
    for(int i = 0; i < n; i++) v[i] = i+1;
    permute(v, 0);

    cout << "---\n";

    // Subsets
    subsets(v);

    cout << "---\n";

    // Backtracking count
    visited.assign(n, false);
    solve(0);
    cout << ans << " permutations\n";

    return 0;
}

// Sample Input:
// 3
//
// Sample Output:
// 1 2 3
// 1 3 2
// 2 1 3
// 2 3 1
// 3 2 1
// 3 1 2
// ---
// { }
// { 1 }
// { 2 }
// { 1 2 }
// { 3 }
// { 1 3 }
// { 2 3 }
// { 1 2 3 }
// ---
// 6 permutations