This repository has been archived on 2022-06-22. You can view files and clone it, but cannot push or open issues or pull requests.
usaco-guide/content/3_Bronze/Complete_Search.md
2020-06-17 15:18:07 -07:00

6.9 KiB

id title author
complete-search Complete Search Darren Yao

In many problems (especially in Bronze), it's sufficient to check all possible cases in the solution space, whether it be all elements, all pairs of elements, or all subsets, or all permutations. Unsurprisingly, this is called complete search (or brute force), because it completely searches the entire solution space.

Example 1

Statement

You are given N (3 \leq N \leq 5000) integer points on the coordinate plane. Find the square of the maximum Euclidean distance (aka length of the straight line) between any two of the points.

Input Format

The first line contains an integer N.

The second line contains N integers, the $x$-coordinates of the points: x_1, x_2, \dots, x_N (-1000 \leq x_i \leq 1000).

The third line contains N integers, the $y$-coordinates of the points: y_1, y_2, \dots, y_N (-1000 \leq y_i \leq 1000).

Output Format

Print one integer, the square of the maximum Euclidean distance between any two of the points.

Solution

We can brute-force every pair of points and find the square of the distance between them, by squaring the formula for Euclidean distance: \text{distance}^2 = (x_2-x_1)^2 + (y_2-y_1)^2. Thus, we store the coordinates in arrays \mintinline{java}{X[]} and \mintinline{java}{Y[]}, such that \mintinline{java}{X[i]} and \mintinline{java}{Y[i]} are the $x$- and $y$-coordinates of the i_{th} point, respectively. Then, we iterate through all possible pairs of points, using a variable max to store the maximum square of distance between any pair seen so far, and if the square of the distance between a pair is greater than our current maximum, we set our current maximum to it.

Java:

int max = 0; // storing the current maximum
for(int i = 0; i < n; i++){ // for each first point
    for(int j = i+1; j < n; j++){ // for each second point
        int dx = x[i] - x[j];
        int dy = y[i] - y[j];
        max = Math.max(max, dx*dx + dy*dy);
        // if the square of the distance between the two points is greater than
        // our current maximum, then update the maximum
    }
}
pw.println(max);

C++

int high = 0; // storing the current maximum
for(int i = 0; i < n; i++){ // for each first point
    for(int j = i+1; j < n; j++){ // for each second point
        int dx = x[i] - x[j];
        int dy = y[i] - y[j];
        high = max(high, dx*dx + dy*dy);
        // if the square of the distance between the two points is greater than
        // our current maximum, then update the maximum
    }
}
cout << high << endl;

A couple notes: first, since we're iterating through all pairs of points, we start the j loop from j = i+1 so that point i and point j are never the same point. Furthermore, it makes it so that each pair is only counted once. In this problem, it doesn't matter whether we double-count pairs or whether we allow i and j to be the same point, but in other problems where we're counting something rather than looking at the maximum, it's important to be careful that we don't overcount. Secondly, the problem asks for the square of the maximum Euclidean distance between any two points. Some students may be tempted to maintain the maximum distance in a variable, and then square it at the end when outputting. However, the problem here is that while the square of the distance between two integer points is always an integer, the distance itself isn't guaranteed to be an integer. Thus, we'll end up shoving a non-integer value into an integer variable, which truncates the decimal part. Using a floating point variable isn't likely to work either, due to precision errors (use of floating point decimals should generally be avoided when possible).

(uh, have you verified this claim?)

Generating Permutations

A permutation is a reordering of a list of elements. Some problems will ask for an ordering of elements that satisfies certain conditions. In these problems, if N \leq 10, we can probably iterate through all permutations and check each permutation for validity. For a list of N elements, there are N! ways to permute them, and generally we'll need to read through each permutation once to check its validity, for a time complexity of O(N \cdot N!).

In Java, we'll have to implement this ourselves, which is called Heap's Algorithm (no relation to the heap data structure). What's going to be in the check function depends on the problem, but it should verify whether the current permutation satisfies the constraints given in the problem.

As an example, here are the permutations generated by Heap's Algorithm for [1, 2, 3]:

[1, 2, 3], [2, 1, 3], [3, 1, 2], [1, 3, 2], [2, 3, 1], [3, 2, 1]

Code for iterating over all permutations is as follows:

// this method is called with k equal to the length of arr
static void generate(int[] arr, int k){
    if(k == 1){
        check(arr); // check the current permutation for validity
    } else {
        generate(arr, k-1);
        for(int i = 0; i < k-1; i++){
            if(k % 2 == 0){
                swap(arr, i, k-1);
                // swap indices i and k-1 of arr
            } else {
                swap(arr, 0, k-1);
                // swap indices 0 and k-1 of arr
            }
        }
    }
}

In C++, we can just use the next_permutation() function. To iterate through all permutations, place this inside a do-while loop.

do {
    check(v); // process or check the current permutation for validity
} while(next_permutation(v.begin(), v.end()));

Problems