0% found this document useful (0 votes)
4 views6 pages

Printingbinarysearchpdf

Divide and Conquer (D&C) is a problem-solving strategy that simplifies a problem by dividing it into smaller sub-problems, solving each one, and then combining the results. The document discusses the application of D&C in algorithms such as Binary Search, highlighting its efficiency and versatility in various contexts, including uncommon data structures and the bisection method. It emphasizes the importance of mastering the Binary Search principle for success in programming contests.

Uploaded by

omarandrock
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views6 pages

Printingbinarysearchpdf

Divide and Conquer (D&C) is a problem-solving strategy that simplifies a problem by dividing it into smaller sub-problems, solving each one, and then combining the results. The document discusses the application of D&C in algorithms such as Binary Search, highlighting its efficiency and versatility in various contexts, including uncommon data structures and the bisection method. It emphasizes the importance of mastering the Binary Search principle for success in programming contests.

Uploaded by

omarandrock
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

3.3.

DIVIDE AND CONQUER 


c Steven & Felix

3.3 Divide and Conquer


Divide and Conquer (abbreviated as D&C) is a problem-solving paradigm in which a problem
is made simpler by ‘dividing’ it into smaller parts and then conquering each part. The steps:

1. Divide the original problem into sub-problems—usually by half or nearly half,


2. Find (sub)-solutions for each of these sub-problems—which are now easier,
3. If needed, combine the sub-solutions to get a complete solution for the main problem.

We have seen examples of the D&C paradigm in the previous sections of this book: Various
sorting algorithms (e.g. Quick Sort, Merge Sort, Heap Sort) and Binary Search in Section
2.2 utilize this paradigm. The way data is organized in Binary Search Tree, Heap, Segment
Tree, and Fenwick Tree in Section 2.3, 2.4.3, and 2.4.4 also relies upon the D&C paradigm.

3.3.1 Interesting Usages of Binary Search


In this section, we discuss the D&C paradigm in the well-known Binary Search algorithm.
We classify Binary Search as a ‘Divide’ and Conquer algorithm although one reference [40]
suggests that it should be actually classified as ‘Decrease (by-half)’ and Conquer as it does
not actually ‘combine’ the result. We highlight this algorithm because many contestants
know it, but not many are aware that it can be used in many other non-obvious ways.

Binary Search: The Ordinary Usage


Recall that the canonical usage of Binary Search is searching for an item in a static sorted
array. We check the middle of the sorted array to determine if it contains what we are
looking for. If it is or there are no more items to consider, stop. Otherwise, we can decide
whether the answer is to the left or right of the middle element and continue searching.
As the size of search space is halved (in a binary fashion) after each check, the complexity
of this algorithm is O(log n). In Section 2.2, we have seen that there are built-in library
routines for this algorithm, e.g. the C++ STL algorithm::lower bound (and the Java
Collections.binarySearch).
This is not the only way to use binary search. The pre-requisite for performing a binary
search—a static sorted sequence (array or vector)—can also be found in other uncommon
data structures such as in the root-to-leaf path of a tree (not necessarily binary nor complete)
that satisfies the min heap property. This variant is discussed below.

Binary Search on Uncommon Data Structures


This original problem is titled ‘My Ancestor’ and was used in the Thailand ICPC National
Contest 2009. Abridged problem description: Given a weighted (family) tree of up to N ≤
80K vertices with a special trait: Vertex values are increasing from root to leaves. Find
the ancestor vertex closest to the root from a starting vertex v that has weight at least P .
There are up to Q ≤ 20K such offline queries. Examine Figure 3.3 (left). If P = 4, then
the answer is the vertex labeled with ‘B’ with value 5 as it is the ancestor of vertex v that
is closest to root ‘A’ and has a value of ≥ 4. If P = 7, then the answer is ‘C’, with value 7.
If P ≥ 9, there is no answer.
The naı̈ve solution is to perform a linear O(N) scan per query: Starting from the given
vertex v, we move up the (family) tree until we reach the first vertex whose direct parent
has value < P or until we reach the root. If this vertex has value ≥ P and it is not vertex v

84
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

Figure 3.3: My Ancestor (all 5 root-to-leaf paths are sorted)

itself, we have found the solution. As there are Q queries, this approach runs in O(QN) (the
input tree can be a sorted linked list, or rope, of length N) and will get a TLE as N ≤ 80K
and Q ≤ 20K.
A better solution is to store all the 20K queries (we do not have to answer them im-
mediately). Traverse the tree just once starting from the root using the O(N) preorder
tree traversal algorithm (Section 4.7.2). This preorder tree traversal is slightly modified to
remember the partial root-to-current-vertex sequence as it executes. The array is always
sorted because the vertices along the root-to-current-vertex path have increasing weights,
see Figure 3.3 (right). The preorder tree traversal on the tree shown in Figure 3.3 (left)
produces the following partial root-to-current-vertex sorted array: {{3}, {3, 5}, {3, 5, 7},
{3, 5, 7, 8}, backtrack, {3, 5, 7, 9}, backtrack, backtrack, backtrack, {3, 8}, backtrack,
{3, 6}, {3, 6, 20}, backtrack, {3, 6, 10}, and finally {3, 6, 10, 20}, backtrack, backtrack,
backtrack (done)}.
During the preorder traversal, when we land on a queried vertex, we can perform a
O(log N) binary search (to be precise: lower bound) on the partial root-to-current-vertex
weight array to obtain the ancestor closest to the root with a value of at least P , recording
these solutions. Finally, we can perform a simple O(Q) iteration to output the results. The
overall time complexity of this approach is O(Q log N), which is now manageable given the
input bounds.

Bisection Method
We have discussed the applications of Binary Searches in finding items in static sorted
sequences. However, the binary search principle4 can also be used to find the root of a
function that may be difficult to compute directly.
Example: You buy a car with loan and now want to pay the loan in monthly installments
of d dollars for m months. Suppose the value of the car is originally v dollars and the bank
charges an interest rate of i% for any unpaid loan at the end of each month. What is the
amount of money d that you must pay per month (to 2 digits after the decimal point)?
Suppose d = 576.19, m = 2, v = 1000, and i = 10%. After one month, your debt
becomes 1000 × (1.1) − 576.19 = 523.81. After two months, your debt becomes 523.81 ×
(1.1) − 576.19 ≈ 0. If we are only given m = 2, v = 1000, and i = 10%, how would we
determine that d = 576.19? In other words, find the root d such that the debt payment
function f (d, m, v, i) ≈ 0.
An easy way to solve this root finding problem is to use the bisection method. We pick
a reasonable range as a starting point. We want to fix d within the range [a..b] where
4
We use the term ‘binary search principle’ to refer to the D&C approach of halving the range of possible
answers. The ‘binary search algorithm’ (finding index of an item in a sorted array), the ‘bisection method’
(finding the root of a function), and ‘binary search the answer’ (discussed in the next subsection) are all
instances of this principle.

85
3.3. DIVIDE AND CONQUER 
c Steven & Felix

a = 0.01 as we have to pay at least one cent and b = (1 + i%) × v as the earliest we can
complete the payment is m = 1 if we pay exactly (1 + i%) × v dollars after one month. In
this example, b = (1 + 0.1) × 1000 = 1100.00 dollars. For the bisection method to work5 ,
we must ensure that the function values of the two extreme points in the initial Real range
[a..b], i.e. f (a) and f (b) have opposite signs (this is true for the computed a and b above).

a b d = a+b
2
status: f (d, m, v, i) action
0.01 1100.00 550.005 undershoot by 54.9895 increase d
550.005 1100.00 825.0025 overshoot by 522.50525 decrease d
550.005 825.0025 687.50375 overshoot by 233.757875 decrease d
550.005 687.50375 618.754375 overshoot by 89.384187 decrease d
550.005 618.754375 584.379688 overshoot by 17.197344 decrease d
550.005 584.379688 567.192344 undershoot by 18.896078 increase d
567.192344 584.379688 575.786016 undershoot by 0.849366 increase d
... ... ... a few iterations later . . . ...
... ... 576.190476 stop; error is now less than  answer = 576.19

Table 3.1: Running Bisection Method on the Example Function

Notice that bisection method only requires O(log2 ((b − a)/)) iterations to get an answer
that is good enough (the error is smaller than the threshold error  that we can tolerate).
In this example, bisection method only takes log2 1099.99/ tries. Using a small  = 1e-9,
this yields only ≈ 40 iterations. Even if we use a smaller  = 1e-15, we will still only need
≈ 60 tries. Notice that the number of tries is small. The bisection method is much more
efficient compared to exhaustively evaluating each possible value of d =[0.01..1100.00]/
for this example function. Note: The bisection method can be written with a loop that tries
the values of d ≈ 40 to 60 times (see our implementation in the ‘binary search the answer’
discussion below).

Binary Search the Answer


The abridged version of UVa 11935 - Through the Desert is as follows: Imagine that you are
an explorer trying to cross a desert. You use a jeep with a ‘large enough’ fuel tank – initially
full. You encounter a series of events throughout your journey such as ‘drive (that consumes
fuel)’, ‘experience gas leak (further reduces the amount of fuel left)’, ‘encounter gas station
(allowing you to refuel to the original capacity of your jeep’s fuel tank)’, ‘encounter mechanic
(fixes all leaks)’, or ‘reach goal (done)’. You need to determine the smallest possible fuel
tank capacity for your jeep to be able to reach the goal. The answer must be precise to three
digits after decimal point.
If we know the jeep’s fuel tank capacity, then this problem is just a simulation problem.
From the start, we can simulate each event in order and determine if the goal can be reached
without running out of fuel. The problem is that we do not know the jeep’s fuel tank
capacity—this is the value that we are looking for.
From the problem description, we can compute that the range of possible answers is
between [0.000..10000.000], with 3 digits of precision. However, there are 10M such
possibilities. Trying each value sequentially will get us a TLE verdict.
Fortunately, this problem has a property that we can exploit. Suppose that the correct
answer is X. Setting your jeep’s fuel tank capacity to any value between [0.000..X-0.001]
5
Note that the requirements for the bisection method (which uses the binary search principle) are slightly
different from the binary search algorithm which needs a sorted array.

86
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

will not bring your jeep safely to the goal event. On the other hand, setting your jeep fuel
tank volume to any value between [X..10000.000] will bring your jeep safely to the goal
event, usually with some fuel left. This property allows us to binary search the answer X!
We can use the following code to obtain the solution for this problem.

#define EPS 1e-9 // this value is adjustable; 1e-9 is usually small enough
bool can(double f) { // details of this simulation is omitted
// return true if the jeep can reach goal state with fuel tank capacity f
// return false otherwise
}

// inside int main()


// binary search the answer, then simulate
double lo = 0.0, hi = 10000.0, mid = 0.0, ans = 0.0;
while (fabs(hi - lo) > EPS) { // when the answer is not found yet
mid = (lo + hi) / 2.0; // try the middle value
if (can(mid)) { ans = mid; hi = mid; } // save the value, then continue
else lo = mid;
}

printf("%.3lf\n", ans); // after the loop is over, we have the answer

Note that some programmers choose to use a constant number of refinement iterations
instead of allowing the number of iterations to vary dynamically to avoid precision errors
when testing fabs(hi - lo) > EPS and thus being trapped in an infinite loop. The only
changes required to implement this approach are shown below. The other parts of the code
are the same as above.

double lo = 0.0, hi = 10000.0, mid = 0.0, ans = 0.0;


for (int i = 0; i < 50; i++) { // log_2 ((10000.0 - 0.0) / 1e-9) ~= 43
mid = (lo + hi) / 2.0; // looping 50 times should be precise enough
if (can(mid)) { ans = mid; hi = mid; }
else lo = mid;
}

Exercise 3.3.1.1: There is an alternative solution for UVa 11935 that does not use ‘binary
search the answer’ technique. Can you spot it?
Exercise 3.3.1.2*: The example shown here involves binary-searching the answer where
the answer is a floating point number. Modify the code to solve ‘binary search the answer’
problems where the answer lies in an integer range!

Remarks About Divide and Conquer in Programming Contests


The Divide and Conquer paradigm is usually utilized through popular algorithms that rely
on it: Binary Search and its variants, Merge/Quick/Heap Sort, and data structures: Binary
Search Tree, Heap, Segment Tree, Fenwick Tree, etc. However—based on our experience,
we reckon that the most commonly used form of the Divide and Conquer paradigm in

87
3.3. DIVIDE AND CONQUER 
c Steven & Felix

programming contests is the Binary Search principle. If you want to do well in programming
contests, please spend time practicing the various ways to apply it.
Once you are more familiar with the ‘Binary Search the Answer’ technique discussed in
this section, please explore Section 8.4.1 for a few more programming exercises that use this
technique with other algorithm that we will discuss in the latter parts of this book.
We notice that there are not that many D&C problems outside of our binary search
categorization. Most D&C solutions are ‘geometry-related’ or ‘problem specific’, and thus
cannot be discussed in detail in this book. However, we will encounter some of them in
Section 8.4.1 (binary search the answer plus geometry formulas), Section 9.14 (Inversion
Index), Section 9.21 (Matrix Power), and Section 9.29 (Selection Problem).

Programming Exercises solvable using Divide and Conquer:


• Binary Search
1. UVa 00679 - Dropping Balls (binary search; bit manipulation solutions exist)
2. UVa 00957 - Popes (complete search + binary search: upper bound)
3. UVa 10077 - The Stern-Brocot ... (binary search)
4. UVa 10474 - Where is the Marble? (simple: use sort and then lower bound)
5. UVa 10567 - Helping Fill Bates * (store increasing indices of each char
of ‘S’ in 52 vectors; for each query, binary search for the position of the char
in the correct vector)
6. UVa 10611 - Playboy Chimp (binary search)
7. UVa 10706 - Number Sequence (binary search + some mathematical insights)
8. UVa 10742 - New Rule in Euphomia (use sieve; binary search)
9. UVa 11057 - Exact Sum * (sort, for price p[i], check if price (M - p[i])
exists with binary search)
10. UVa 11621 - Small Factors (generate numbers with factor 2 and/or 3, sort,
upper bound)
11. UVa 11701 - Cantor (a kind of ternary search)
12. UVa 11876 - N + NOD (N) ([lower|upper] bound on sorted sequence N)
13. UVa 12192 - Grapevine * (the input array has special sorted properties;
use lower bound to speed up the search)
14. Thailand ICPC National Contest 2009 - My Ancestor (author: Felix Halim)
• Bisection Method or Binary Search the Answer
1. UVa 10341 - Solve It * (bisection method discussed in this section; for al-
ternative solutions, see http://www.algorithmist.com/index.php/UVa 10341)
2. UVa 11413 - Fill the ... * (binary search the answer + simulation)
3. UVa 11881 - Internal Rate of Return (bisection method)
4. UVa 11935 - Through the Desert (binary search the answer + simulation)
5. UVa 12032 - The Monkey ... * (binary search the answer + simulation)
6. UVa 12190 - Electric Bill (binary search the answer + algebra)
7. IOI 2010 - Quality of Living (binary search the answer)
Also see: Divide & Conquer for Geometry Problems (see Section 8.4.1)
• Other Divide & Conquer Problems
1. UVa 00183 - Bit Maps * (simple exercise of Divide and Conquer)
2. IOI 2011 - Race (D&C; whether the solution path uses a vertex or not)
Also see: Data Structures with Divide & Conquer flavor (see Section 2.3)

88
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

3.4 Greedy
An algorithm is said to be greedy if it makes the locally optimal choice at each step with the
hope of eventually reaching the globally optimal solution. In some cases, greedy works—the
solution is short and runs efficiently. For many others, however, it does not. As discussed
in other typical Computer Science textbooks, e.g. [7, 38], a problem must exhibit these two
properties in order for a greedy algorithm to work:
1. It has optimal sub-structures.
Optimal solution to the problem contains optimal solutions to the sub-problems.
2. It has the greedy property (difficult to prove in time-critical contest environment!).
If we make a choice that seems like the best at the moment and proceed to solve the
remaining subproblem, we reach the optimal solution. We will never have to reconsider
our previous choices.

3.4.1 Examples
Coin Change - The Greedy Version
Problem description: Given a target amount V cents and a list of denominations of n coins,
i.e. we have coinValue[i] (in cents) for coin types i ∈ [0..n-1], what is the minimum
number of coins that we must use to represent amount V ? Assume that we have an unlimited
supply of coins of any type. Example: If n = 4, coinValue = {25, 10, 5, 1} cents6 , and
we want to represent V = 42 cents, we can use this Greedy algorithm: Select the largest
coin denomination which is not greater than the remaining amount, i.e. 42-25 = 17 → 17-10
= 7 → 7-5 = 2 → 2-1 = 1 → 1-1 = 0, a total of 5 coins. This is optimal.
The problem above has the two ingredients required for a successful greedy algorithm:
1. It has optimal sub-structures.
We have seen that in our quest to represent 42 cents, we used 25+10+5+1+1.
This is an optimal 5-coin solution to the original problem!
Optimal solutions to sub-problem are contained within the 5-coin solution, i.e.
a. To represent 17 cents, we can use 10+5+1+1 (part of the solution for 42 cents),
b. To represent 7 cents, we can use 5+1+1 (also part of the solution for 42 cents), etc
2. It has the greedy property: Given every amount V , we can greedily subtract from it
the largest coin denomination which is not greater than this amount V . It can be
proven (not shown here for brevity) that using any other strategies will not lead to an
optimal solution, at least for this set of coin denominations.
However, this greedy algorithm does not work for all sets of coin denominations. Take for
example {4, 3, 1} cents. To make 6 cents with that set, a greedy algorithm would choose 3
coins {4, 1, 1} instead of the optimal solution that uses 2 coins {3, 3}. The general version
of this problem is revisited later in Section 3.5.2 (Dynamic Programming).

UVa 410 - Station Balance (Load Balancing)


Given 1 ≤ C ≤ 5 chambers which can store 0, 1, or 2 specimens, 1 ≤ S ≤ 2C specimens
and a list M of the masses of the S specimens, determine which chamber should store each
specimen in order to minimize ‘imbalance’. See Figure 3.4 for a visual explanation7 .
6
The presence of the 1-cent coin ensures that we can always make every value.
7
Since C ≤ 5 and S ≤ 10, we can actually use a Complete Search solution for this problem. However,
this problem is simpler to solve using the Greedy algorithm.

89

You might also like