Complete Search

beginner25 min

The idea

If the number of possible answers is small enough, check them all. This is complete search, also called brute force, and it solves a large fraction of entry-level problems.

It is not a fallback for when you cannot find something better. When the constraints permit it, it is the intended solution — it is easier to write correctly and easier to reason about than a clever alternative.

Deciding whether it fits

Count the possibilities, multiply by the work per possibility, and compare against roughly 108 operations.

Search space sizes

StructureCountFeasible when
Every single elementNAlways
Every pairN2/2N ≤ 10,000
Every tripleN3/6N ≤ 500
Every subset2NN ≤ 24
Every permutationN!N ≤ 10
Every value in a rangerange sizerange ≤ 107
Every pair of grid cells(RC)2RC ≤ 3,000

Read the constraint, find the matching row, and you know whether to write the loops. A problem with N ≤ 20 and a yes/no answer about subsets is telling you to enumerate subsets.

Nested loops over pairs and triples

The most common form. Fix each combination and evaluate it.

Given N integers, find the largest sum of two distinct elements.

int best = Integer.MIN_VALUE;
for (int i = 0; i < n; i++) {
    for (int j = i + 1; j < n; j++) {
        best = Math.max(best, a[i] + a[j]);
    }
}
System.out.println(best);

Starting the inner loop at i + 1 rather than 0 does two things: it avoids pairing an element with itself, and it visits each unordered pair exactly once instead of twice.

Loop bounds for combinations

  • j = i + 1 — each unordered pair once. Use when (i, j) and (j, i) are the same thing.
  • j = 0 with a j != i guard — each ordered pair once. Use when order matters.
  • j = i — pairs including an element with itself. Use when reuse is allowed.

Choosing the wrong one usually still produces a plausible-looking answer, so it is worth being deliberate.

Initializing best to Integer.MIN_VALUE rather than 0 matters when all values are negative. Initializing an accumulator to a value that could be the answer is a recurring bug.

Enumerating subsets with bitmasks

When each of N items is either chosen or not, there are 2N combinations, and each maps to the bits of an integer.

Given N ≤ 20 weights, is there a subset summing to exactly target?

boolean possible = false;
for (int mask = 0; mask < (1 << n); mask++) {
    long sum = 0;
    for (int i = 0; i < n; i++) {
        if ((mask & (1 << i)) != 0) {
            sum += w[i];
        }
    }
    if (sum == target) {
        possible = true;
        break;
    }
}

1 << n is 2n. Bit i of mask being set means item i is in the subset. The test (mask & (1 << i)) != 0 checks that bit.

Bitmask notes

  • 1 << n overflows int for n ≥ 31. For n up to 24 you are fine; beyond that, 2N is too slow anyway.
  • Write (mask & (1 << i)) != 0, not == 1. The expression yields 1 << i, not 1.
  • Integer.bitCount(mask) gives the subset size in one call.
  • The cost here is O(2N · N) because of the inner loop. For N = 20 that is about 20 million — fine.

Searching over the answer

Sometimes the thing to enumerate is not a subset of the input but the answer itself.

Cows are at given positions. Find the smallest integer position p such that the total distance from all cows to p is at most D.

If coordinates go up to 1,000, try every one:

int answer = -1;
for (int p = 0; p <= 1000; p++) {
    long total = 0;
    for (int i = 0; i < n; i++) {
        total += Math.abs(a[i] - p);
    }
    if (total <= d) {
        answer = p;
        break;
    }
}

This is O(range · N). It works because the coordinate range is small. When the range is large but the property is monotonic — once true, always true — binary search replaces the outer loop. That is covered in Binary Search.

Permutations

When you need every ordering of N ≤ 10 items, generate permutations recursively. The general technique is covered in Recursive Complete Search; for small fixed sizes, nested loops with distinctness checks are simpler:

// every ordered triple of distinct indices
for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++)
        for (int k = 0; k < n; k++)
            if (i != j && j != k && i != k) {
                // evaluate the arrangement (i, j, k)
            }

Pruning

Stopping early when a partial choice already cannot work often turns an infeasible search into a fast one. The complexity does not change on paper, but the practical runtime can drop by orders of magnitude.

for (int mask = 0; mask < (1 << n); mask++) {
    long sum = 0;
    boolean over = false;
    for (int i = 0; i < n; i++) {
        if ((mask & (1 << i)) != 0) {
            sum += w[i];
            if (sum > target) { over = true; break; }   // no point continuing
        }
    }
    if (!over && sum == target) { /* ... */ }
}

Pruning only helps if the condition genuinely rules out the rest of the branch. Adding a check that is almost never true costs time instead of saving it.

Common mistakes

  • Miscounting the search space. Verify the count against the table before writing the code.
  • Off-by-one in loop bounds. mask < (1 << n), not <=.
  • Overflow in the evaluation. Sums inside the loop usually need long.
  • Wrong initial value for a running maximum or minimum.
  • Forgetting that “distinct” is required when the problem says two different elements.

Practice

For each one, first state the size of the search space, then write the loops.

  1. Given N ≤ 1,000 integers, count the pairs that sum to a target X.
  2. Given N ≤ 100 integers, count the triples whose sum is divisible by 3.
  3. Given N ≤ 18 items with weights and values, and a capacity C, find the maximum total value that fits.
  4. Given N ≤ 500 points with integer coordinates, find the pair with the smallest distance. Compare squared distances to stay in integers.
  5. Given N ≤ 20 integers, determine whether they can be split into two groups with equal sums.
Hints
  1. N2/2 = 500,000 pairs. Nested loop with j = i + 1.
  2. N3/6 is about 166,000. Triple nested loop; test sum % 3 == 0.
  3. 218 = 262,144 subsets. For each, sum weight and value; keep the best that fits.
  4. 125,000 pairs. Never take a square root — compare dx*dx + dy*dy, and use long since coordinates may be large.
  5. Enumerate subsets. A subset works if its sum is exactly half the total. Check first that the total is even; if it is odd, the answer is immediately no.

Next