0% found this document useful (0 votes)
14 views65 pages

3 Paradigms

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)
14 views65 pages

3 Paradigms

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/ 65

Problem-

Solving
Paradigms

Greedy
Algorithms

Linear Sweep
Problem-Solving Paradigms
Binary Search COMP4128 Programming Challenges

School of Computer Science and Engineering


UNSW Sydney

Term 3, 2022
Table of Contents 2

Problem-
Solving
Paradigms

Greedy
Algorithms

Linear Sweep
1 Greedy Algorithms
Binary Search

2 Linear Sweep

3 Binary Search
Greedy Algorithms 3

Problem-
Solving
Paradigms

One approach to reduce the number of states explored by


Greedy
Algorithms an algorithm is to simply make the best available choice at
Linear Sweep each stage, and never consider the alternatives
Binary Search

This is known as a greedy strategy

General Principle: Don’t bother with states that will


never contribute to the optimal solution!
Greedy Algorithms 4

Problem-
Solving
Paradigms

It is imperative that you prove (to yourself) that this


Greedy
Algorithms
process achieves the optimal solution, that is, it is not
Linear Sweep
possible to beat the greedy strategy using a suboptimal
Binary Search
choice at any stage.

Look for a natural ordering of states

For some problems, the greedy algorithm is not optimal,


and we instead look to techniques such as dynamic
programming
Example problem: Psychological Jujitsu 5

Problem-
Solving
Paradigms

Problem statement You are playing a 2-player game with


Greedy
Algorithms 2 ≤ N ≤ 1000 rounds. You and your opponent have N
Linear Sweep different cards numbered from 1 to N. In round i, each
Binary Search player picks an unplayed card from their hand. The player
with the higher card wins i points (no points are given for
draws).

Through “psychology” you know exactly what cards your


opponent will play in each round. What is your maximum
possible margin of victory?
Example problem: Psychological Jujitsu 6

Problem-
Solving
Paradigms

Greedy Input An integer N and a permutation of 1 to N, the i-th


Algorithms
value is the card your opponent plays in the i-th round.
Linear Sweep

Binary Search

Output A single integer, your maximum margin of victory


assuming optimal play.

Source Orac
Example problem: Psychological Jujitsu 7

Problem-
Solving
Paradigms

Example Input
Greedy
Algorithms 3
Linear Sweep 3 1 2
Binary Search

Example Output 4

Explanation: Play 1 2 3. You lose the first round (-1) but


win the second and third (+2, +3).
Example problem: Psychological Jujitsu 8

Problem-
Solving
Paradigms

Greedy Brute force? There are N! possible play orders.


Algorithms

Linear Sweep

Binary Search

But maybe we can eliminate many of these play orders as


suboptimal.

For this, it helps to imagine what a possible play order


could look like.
Example problem: Psychological Jujitsu 9

Problem-
Solving
Paradigms
Consider the round where the opponent plays card N.
Greedy
Algorithms

Linear Sweep In such a round, we can either draw (play card N too) or
Binary Search lose.

If we lose, which card should we play?

May as well play our worst card, 1.

But now we can win every other round!


Example problem: Psychological Jujitsu 10

Problem-
Solving
Paradigms Okay, how about the play patterns where we play card N
and draw?
Greedy
Algorithms
Then it’s like we’re repeating the problem with N − 1 in
Linear Sweep
place of N.
Binary Search

Unrolling this recursion, we now see, we can assume our


play pattern is:
Pick a number i.
Draw all rounds with opponent card > i.
Lose the round with card i.
Win all rounds with cards < i.

Only N play patterns! Can simulate each in O(N). Total


O(N2 ) = O(1, 000, 000).
Example problem: Psychological Jujitsu 11

Problem-
Solving
Paradigms Implementation
#include <bits/stdc ++.h>
Greedy using namespace std;
Algorithms
const int MAXN = 1005;
Linear Sweep int N, opp[MAXN ];
Binary Search
int main () {
cin >> N;
for (int i = 0; i < N; i++) cin >> opp[i];
int ans = 0;
for (int i = 1; i <= N; i++) {
// draw > i, lose round i, win rounds < i
int cur = 0;
for (int j = 0; j < N; j++) {
if (opp[j] == i) cur -= j+1;
if (opp[j] < i) cur += j+1;
}
ans = max(ans , cur);
}
cout << ans << '\n';
return 0;
}
Example problem: Psychological Jujitsu 12

Problem-
Solving
Paradigms

Greedy Moral: One way to eliminate states is figure out


Algorithms

Linear Sweep
conditions “good” states must satisfy. For this, it helps to
Binary Search
consider a problem from different angles.

Other angles would have worked too, e.g.


consider which round the opponent plays card 1
consider which round you play card N
etc.
Table of Contents 13

Problem-
Solving
Paradigms

Greedy
Algorithms

Linear Sweep
1 Greedy Algorithms
Binary Search

2 Linear Sweep

3 Binary Search
Linear Sweeps 14

Problem-
Solving
Paradigms
Very basic but fundamental idea. Instead of trying to do a
problem all at once, try to do it in some order that lets
Greedy
you build up state.
Algorithms

Linear Sweep
This lets you process events one by one. This can be
Binary Search easier than trying to handle them all at once.
General Principle: Having an order is better than not
having an order!
Trying to sort and pick the right order to do a problem in
is fundamental.
If there isn’t a natural order to a problem, you may as well
try to do it in any sorted order.
Even if there is a natural order, sometimes it isn’t the
right one!
Example problem: Stabbing 15

Problem-
Solving
Paradigms Problem statement You have a list of closed intervals,
each with an integer start point and end point. For
Greedy
Algorithms
reasons only known to you, you want to stab each of the
Linear Sweep intervals with a knife. To save time, you consider an
Binary Search interval stabbed if you stab any position that is contained
with the interval. What is the minimum number of stabs
necessary to stab all the intervals?

Input The list of intervals, S. 0 ≤ |S| ≤ 1, 000, 000 and


each start point and end point have absolute values less
than 2,000,000,000.

Output A single integer, the minimum number of stabs


needed to stab all intervals.
Example problem: Stabbing 16

Problem-
Solving
Paradigms

Greedy Example
Algorithms

Linear Sweep

Binary Search

The answer here is 3.


Example problem: Stabbing 17

Problem-
Solving
Paradigms

Greedy How do we decide where to stab? State space is again


Algorithms
laughably big.
Linear Sweep

Binary Search

Again let’s ask ourselves if we can eliminate many of the


stab possibilities.

Focus on a single stab for now.


Example problem: Stabbing 18

Problem-
Solving
Paradigms

Greedy
Algorithms
Observation 1: We can move it so it is an end point of
Linear Sweep
an interval without decreasing the set of intervals we stab.
Binary Search

Proof: Consider any solution where there is a stab not at


the endpoint of an interval. Then we can create an
equivalent solution by moving that stab rightwards until it
hits an end point.
Example problem: Stabbing 19

Problem-
Solving
Paradigms

Now let’s try drawing sample data and consider moving


Greedy
Algorithms from left to right. Where do we put our first stab?
Linear Sweep

Binary Search

Observation 2: By Observation 1, we may assume it is at


the first endpoint.
Example problem: Stabbing 20

Problem-
Solving
Paradigms

Greedy Algorithm 1 Stab everything that overlaps with the first


Algorithms
end point. Then, remove those intervals from the intervals
Linear Sweep
to be considered, and recurse on the rest of the intervals.
Binary Search

Complexity There are a few different ways to implement


this idea, since the algorithm’s specifics are not completely
defined. But there is a simple way to implement this
algorithm as written in O(|S|2 ) time.
Example problem: Stabbing 21

Problem-
Solving
Paradigms

Greedy If we look closely at the recursive process, there is an


Algorithms
implicit order in which we will process the intervals:
Linear Sweep
ascending by end point
Binary Search

If we sort the intervals by their end points and can also


efficiently keep track of which intervals have been already
stabbed, we can obtain a fast algorithm to solve this
problem.
Example problem: Stabbing 22

Problem-
Solving
Paradigms Given all the intervals sorted by their end points, what do
we need to keep track of? The last stab point
Greedy
Algorithms
Is this enough? How can we be sure we haven’t missed
Linear Sweep
anything?
Binary Search
Since we always stab the next unstabbed end point, we
can guarantee that there are no unstabbed intervals that
are entirely before our last stab point.
For each next interval we encounter (iterating in ascending
order of end point), that interval can start before or
on/after our last stab point.
If it starts before our last stab point, it is already stabbed,
so we ignore it and continue.
If it starts after our last stab point, then it hasn’t been
stabbed yet, so we should do that.
Example problem: Stabbing 23

Problem-
Solving
Paradigms

Greedy Algorithm 2 Sort the intervals by their end points. Then,


Algorithms
considering these intervals in increasing order, we stab
Linear Sweep
again if we encounter a new interval that doesn’t overlap
Binary Search
with our right most stab point.

Complexity For each interval, there is a constant amount


of work, so the main part of the algorithm runs in O(|S|)
time, O(|S| log|S|) after sorting.
Example problem: Stabbing 24

Problem-
Solving Implementation
Paradigms
#include <iostream >
#include <utility >
#include <algorithm >
Greedy using namespace std;
Algorithms
const int N = 1001001;
Linear Sweep
pair <int , int > victims [N];
Binary Search
int main () {
// scan in intervals as (end , start ) so as to sort by endpoint
int n;
cin >> n;
for (int i = 0; i < n; i++) cin >> victims [i]. second >> victims [i]. first;
sort(victims , victims + n);

int last = -2000000001 , res = 0;


for (int i = 0; i < n; i++) {
// if this interval has been stabbed already , do nothing
if ( victims [i]. second <= last) continue;
// otherwise stab at the endpoint of this interval
res ++;
last = victims [i]. first;
}

cout << res << '\n';


return 0;
}
Example problem: Stabbing 25

Problem-
Solving
Paradigms

Greedy
Algorithms
Moral: Sorting into a sensible order is often helpful. As is
Linear Sweep
drawing pictures.
Binary Search

I often find it helpful to play with a problem on paper and


see how I would solve it manually for small cases.
Example problem: Restaurants 26

Problem-
Solving
Paradigms
Problem statement
Greedy
Algorithms There are N ≤ 2000 countries, the i-th has ai ≤ 20
Linear Sweep delegates.
Binary Search

There are M ≤ 2000 restaurants, the i-th can hold


bi ≤ 100 delegates.

For “synergy” reasons, no restaurant can hold 2 delegates


from the same country.

What’s the minimum number of delegates that need to


starve?
Example problem: Restaurants 27

Problem-
Solving
Paradigms

Greedy Input An integer N, and N integers ai . An integer M, and


Algorithms
M integers bi .
Linear Sweep

Binary Search

Output A single integer, the minimum number of


delegates that need to starve.

Source Orac
Example problem: Restaurants 28

Problem-
Solving
Paradigms
Example Input
Greedy 3
Algorithms
4 3 3
Linear Sweep

Binary Search
3
5 2 3

Example Output 2

Explanation: Someone from the first country starves.


Furthermore, the second restaurant has too few seats.
Example problem: Restaurants 29

Problem-
Solving
Paradigms

Yet again, trying all assignments is laughably slow. So


Greedy
Algorithms
again, let us try to think about what conditions a good
Linear Sweep
assignment may have?
Binary Search

Makes sense to consider all delegates of a country at once


so we don’t have to keep track of who has been assigned
where.

Consider the countries in any arbitrary order. Suppose


“Australia” is the first country we are considering.
Example problem: Restaurants 30

Problem-
Solving
Paradigms Observation 1: We should assign as many Australian
delegates as possible.
Greedy
Algorithms Proof: In any solution that does not, there is some
Linear Sweep restaurant with no Australian delegates and there is a
Binary Search starving Australian delegate.
We can then kick out any delegate for an Australian
delegate without making the solution any worse.
But where should we assign the Australian delegates?
Our main objective is to make it easier to seat the other
country’s delegates.
From some extreme examples, the bottleneck seems to be
the restaurants with few seats.
Example problem: Restaurants 31

Problem-
Solving Observation 2: We should assign delegates to the
Paradigms
restaurants with the most seats remaining.
Greedy Proof: Again, consider a solution that does not.
Algorithms

Linear Sweep
Then we skip restaurant i for a restaurant j where bi > bj .
Binary Search But this means we can swap some delegate from
restaurant i with the Australian delegate in j while

preserving uniqueness.
By repeating these swaps, we obtain a solution just as
optimal except Observation 2 was obeyed.
Example problem: Restaurants 32

Problem-
Solving
Paradigms

Hence we may consider just solutions where Australia’s


Greedy
Algorithms delegates are assigned to the restaurants with the most
Linear Sweep seats remaining.
Binary Search

Now repeat all other countries in the same manner.

One easy way to implement: Sweep through the countries


one by one. For each country, sort the restaurants in
decreasing capacity order and assign to them in that order.
Example problem: Restaurants 33

Problem-
Solving Implementation
Paradigms
#include <bits/stdc ++.h>
using namespace std;
Greedy
Algorithms const int MAXN = 2005, MAXM = 2005;
int N, numDelegates [MAXN], M, numSeats [MAXN ];
Linear Sweep
int main () {
Binary Search cin >> N;
for (int i = 0; i < N; i++) cin >> numDelegates [i];
cin >> M;
for (int i = 0; i < M; i++) cin >> numSeats [i];
int starved = 0;
for (int i = 0; i < N; i++) {
int delegatesRemaining = numDelegates [i];
sort(numSeats , numSeats +M, greater <int >());
for (int j = 0; j < M; j++) {
if ( numSeats [j] > 0 && delegatesRemaining > 0) {
numSeats [j]--;
delegatesRemaining --;
}
}
starved += delegatesRemaining ;
}
cout << starved << '\n';
return 0;
}
Example problem: Restaurants 34

Problem-
Solving
Paradigms

Complexity? O(N) countries. For each we sort a M


Greedy
Algorithms
length list and then a linear sweep.
Linear Sweep

Binary Search
O(NM log M) ≈ O(4mil · 11), fast enough.

Moral: One way to make observations is think abstractly


about what should hold. Often this is guided by examples.

Once you have some guess, you can try to prove it after.
Coordinate Compression 35

Problem-
Solving
Paradigms Most of the examples in class have coordinates only up to
100,000 or so. But for most examples this is just a
Greedy
Algorithms
niceness condition.
Linear Sweep

Binary Search For most algorithms, the actual values of coordinates is


irrelevant, just the relative order.

So if coordinates are up to 1 billion but there are


N ≤ 100, 000 points then usually there are only O(N)
interesting coordinates and we are bottle necked by O(N).

E.g: range queries on a set of points. I don’t care exactly


what the coordinates of the points or query is, just which
points are within the query’s range.
Coordinate Compression 36

Problem-
Solving
Paradigms Coordinate compression is the idea of replacing each
coordinate by its rank among all coordinates. Hence we
Greedy preserve the relative order of values while making the
Algorithms

Linear Sweep
maximum coordinate O(N).
Binary Search
This reduces us to the case with bounded coordinates.

A few ways to implement this in O(N log N). E.g: sort,


map, order statistics tree.

I prefer one of the latter 2, since the data structure helps


you convert between the compressed and uncompressed
coordinates if needed (e.g: when querying).

Also with the former, one needs to be careful of equality.


Coordinate Compression 37

Problem-
Solving
Paradigms

#include <bits/stdc ++.h>


Greedy using namespace std;
Algorithms
// coordinates -> ( compressed coordinates ).
Linear Sweep
map <int , int > coordMap ;
Binary Search
void compress (vector <int >& values) {
for (int v : values) {
coordMap [v] = 0;
}
int cId = 0;
for (auto it = coordMap .begin (); it != coordMap .end (); ++it) {
it ->second = cId ++;
}
for (int &v : values) {
v = coordMap [v];
}
}
Table of Contents 38

Problem-
Solving
Paradigms

Greedy
Algorithms

Linear Sweep
1 Greedy Algorithms
Binary Search

2 Linear Sweep

3 Binary Search
Binary Search 39

Problem-
Solving
Paradigms
Surprisingly powerful technique!
Greedy
Algorithms

Linear Sweep You should have seen binary search in the context of
Binary Search searching an array before.

For us, the power comes from binary searching on


non-obvious functions instead.

Key problem: Given a monotone function, find the


largest/smallest x such that f(x) is less than/greater
than/equal to/... y.
Aside: Binary Search Implementation 40

Problem-
Solving
Paradigms

Hands up if you’ve ever messed up a binary search


Greedy
Algorithms implementation.
Linear Sweep

Binary Search

I think binary search is notorious for having annoying


off-by-1s and possible infinite loops.

Many ways to implement so pick one you’re confident you


can code with no thought. I’ll present the one I use which
I find avoids all these annoying corner cases.
Aside: Binary Search Implementation 41

Problem-
Solving
Paradigms

#include <bits/stdc ++.h>


using namespace std;
Greedy
Algorithms
// Find the smallest X such that f(X) is true;
Linear Sweep int binarysearch (function <bool(int)> f) {
int lo = 0;
Binary Search
int hi = 100000;
int bestSoFar = -1;
// Range [lo , hi ];
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (f(mid)) {
bestSoFar = mid;
hi = mid - 1;
} else {
lo = mid + 1;
}
}
return bestSoFar ;
}
Aside: Binary Search Implementation 42

Problem-
Solving
Paradigms

Greedy The best way to implement a binary search is not to


Algorithms
implement it at all.
Linear Sweep

Binary Search

If you are binary searching a range of explicit values, e.g.


integers in a sorted array, use functions from
<algorithm> to avoid bugs from writing your own.
binary_search
lower_bound
upper_bound
Aside: Binary Search Implementation 43

Problem-
Solving
Paradigms

#include <algorithm >


Greedy #include <iostream >
Algorithms using namespace std;

Linear Sweep const int N = 100100;


int a[N];
Binary Search
int main () {
int n;
cin >> n;
assert(n <= N);
for (int i = 0; i < n; i++)
cin >> a[i];
assert( is_sorted (a, a+n));

int x;
cin >> x;
bool found = binary_search (a, a+n, x);
cout << (found ? "found " : "did not find ") << x;
Aside: Binary Search Implementation 44

Problem-
Solving
Paradigms

int y;
Greedy cin >> y;
Algorithms int i = lower_bound (a, a+n, y) - a;
if (i < n)
Linear Sweep
cout << "a[" << i << "] = " << a[i] << " is the first entry to compare >= "
Binary Search << y;
else
cout << "all elements of a[] compare < " << y;

int z;
cin >> z;
int j = upper_bound (a,a+n,z) - a;
if (j < n)
cout << "a[" << j << "] = " << a[j] << " is the first entry to compare > "
<< z;
else
cout << "all elements of a[] compare <= " << z;
}
Decision Problems and Optimisation Problems 45

Problem-
Solving
Paradigms
Decision problems are of the form
Given some parameters including X, can you ...
Greedy
Algorithms

Linear Sweep

Binary Search
Optimisation problems are of the form:
What is the smallest X for which you can ...

An optimisation problem is typically much harder than the


corresponding decision problem, because there are many
more choices

Can we reduce (some) optimisation problems to decision


problems?
Discrete Binary Search 46

Problem-
Solving Let f(X) be the outcome of the decision problem for a
Paradigms
given X, so f is an integer valued function with range
Greedy
{0, 1}.
Algorithms
It is sometimes (but not always) the case in such problems
Linear Sweep
that increasing X does not make it any harder for the
Binary Search
condition to hold (i.e: that if the condition holds with X
then it also holds with X + 1).
Thus f is all 0’s up to the first 1, after which it is all 1’s.
This is a monotonic function, so we can use binary search!
This technique of binary searching the answer, that is,
finding the smallest X such that f(X) = 1 using binary
search, is often called discrete binary search.
Overhead is just a factor of O(log A) where A is the range
of possible answers.
Example: Chocolate Bar 47

Problem-
Solving
Paradigms

Problem Statement: You have a bar of chocolate with N


Greedy squares, each square has a tastiness ti . You have K
Algorithms

Linear Sweep
friends. Break the bar into K contiguous pieces. The
Binary Search
overall happiness of the group is the minimum total
tastiness of any of these K pieces. What’s the maximum
overall happiness you can achieve?

Input Format: First line, 2 integers, N, K with


1 ≤ K ≤ N ≤ 1, 000, 000. The next line will contain N
integers, ti , the tastiness of the ith piece. For all i,
1 ≤ i ≤ 100, 000.
Example: Chocolate Bar 48

Problem-
Solving
Paradigms

Sample Input:
Greedy
Algorithms 5 2
Linear Sweep 9 7 3 7 4
Binary Search

Sample Output:
14

Explanation: Break the bar into the first 2 squares and


the last 3 squares.
Example: Chocolate Bar 49

Problem-
Solving
Paradigms

Greedy
Algorithms It is worth trying to approach the minimization problem
Linear Sweep directly, just to appreciate the difficulty.
Binary Search

The problem is there’s no greedy choices you can make.


It’s impossible to determine where the first cut should end.
You can try a DP but the state space is large.
Example: Chocolate Bar 50

Problem-
Solving
Paradigms We are asked to maximize the minimum sum of the K
pieces.
Greedy
Algorithms

Linear Sweep
Let’s turn this into asking about a decision problem.
Binary Search
Define b(X) to be True iff we can split the bar into K
pieces, each with sum at least X.

Then the problem is asking for the largest X such that


b(X) is True.

Note: We define it to be at least X. This makes it


monotone. If we instead defined it as exactly X then the
function is too messy to be useful.
Example: Chocolate Bar 51

Problem-
Solving
Paradigms
Rephrased Problem: Define b(X) to be True iff we can
Greedy
split the bar into K pieces, each with sum at least X.
Algorithms What is the largest X such that b(X) is True?
Linear Sweep

Binary Search
Key(and trivial) Observation: b(X) is non-increasing.

So we can binary search over b(X). Hence to find the


maximum such X, it suffices to be able to calculate b(X)
quickly.

New Problem: Can I split the bar into K pieces, each


with sum at least A?
Example: Chocolate Bar 52

Problem-
Solving
Paradigms
New Problem: Can I split the bar into K pieces, each
with sum at least A?
Greedy
Algorithms Note that we can rephrase this into a maximization
Linear Sweep question. Given each piece has sum at least A, what is the
Binary Search maximum number of pieces I can split the bar into?
Let’s try going one piece at a time. What should the first
piece look like?
Key Observation: It should be the minimum length
possible while having total ≥ A.
This applies for all the pieces.
So to get the maximum number of pieces needed, we
sweep left to right making each piece as short as possible.
Example: Chocolate Bar 53

Problem-
Solving
Paradigms

#include <bits/stdc ++.h>


Greedy using namespace std;
Algorithms
const int MAXN = 1000000;
Linear Sweep
int N, K; long long bar[MAXN ];
Binary Search
bool canDo(long long A) {
long long cPiece = 0;
int nPieces = 0;
for (int i = 0; i < N; i++) {
cPiece += bar[i];
if (cPiece >= A) {
nPieces ++;
cPiece = 0;
}
}
return nPieces >= K;
}
Example: Chocolate Bar 54

Problem-
Solving
Paradigms

int main () {
Greedy scanf("%d %d", &N, &K);
Algorithms for (int i = 0; i < N; i++) scanf("%lld", &bar[i]);
long long lo = 1;
Linear Sweep
long long hi = 1e12;
Binary Search long long ans = -1;
while (lo <= hi) {
long long mid = (lo + hi) / 2;
// Trying to find the highest value that is feasible :
if (canDo(mid)) {
ans = mid;
lo = mid + 1;
} else {
hi = mid - 1;
}
}
printf("%lld\n", ans);
}
Example: Chocolate Bar 55

Problem-
Solving Complexity? O(N log A) where A is max answer.
Paradigms
This problem and solution is very typical of binary search
Greedy
problems.
Algorithms
To start with, you are asked to maximize a value.
Linear Sweep
But we can rephrase it into maximizing a value that
Binary Search
satisfies a decision problem! In forming the decision
problem, you ask if the answer could be at least A, not
just exactly A.
Now with the minimum tastiness of each bar fixed, you
now switch to trying to maximize the number of pieces
you can make. And this can be greedied since we know
how small we can make each piece.
Notice why fixing A made the problem easier. Because we
had one less parameter influencing our choices and we
could make greedy decisions now.
Binary Search 56

Problem-
Solving
Paradigms One of the most common places binary search appears is
in problems that ask us to maximize the minimum of
Greedy something (or minimize the maximum of something).
Algorithms

Linear Sweep Another way to see if it’s useful is just to see if the
Binary Search quantity you are minimizing is monotone.

And this is very common! Usually, you are told to minimize


a value because the problem only gets easier if it increases.

Until you get the hang of it, it’s worth just always trying
to apply it.

At worst, the decision problem can’t be any harder than


the optimization problem (though it may lead you down a
dead end).
Aside: Ternary Search 57

Problem-
Solving
Paradigms Ternary search also exists. It applies to finding the
maximum of a function that strictly increases to a peak,
Greedy stays the same, then strictly decreases. Note the strictlys.
Algorithms

Linear Sweep Instead of splitting the range in 2, we instead now split it


Binary Search
into 3 by querying 2 points. At each step we discard one
of the thirds based on comparison of the 2 points.

Alternatively, we can usually binary search the derivative.


Usually this is the discrete form of the derivative (binary
search on h(x) := f(x + 1) − f(x)).

Appears much less often so won’t talk about it more but it


is a useful thing to know exists.

Exercise left to the reader to figure it out!


Example: Robot Moves 58

Problem-
Solving
Paradigms Problem Statement: You have just created a robot that
will revolutionize RoboCup forever. Well 1D RoboCup at
Greedy
Algorithms least.
Linear Sweep
The robot starts at position 0 on a line and can perform
Binary Search
three types of moves:
L: Move left by 1 position.
R: Move right by 1 position.
S: Stand still.
Currently the robot already has a loaded sequence of
instructions.
You need to get the robot to position x. To do so, you can
replace a single contiguous subarray of the robot’s
instructions. What is the shortest subarray you can replace
to get the robot to position x?
Example: Robot Moves 59

Problem-
Solving
Paradigms Input Format: First line, 2 integers, n, x, the length of
the loaded sequence and the destination.
Greedy 1 ≤ |x| ≤ n ≤ 200, 000. The next line describes the loaded
Algorithms

Linear Sweep
sequence.
Binary Search
Sample Input:
5 -4
LRRLR

Sample Output:
4

Explanation: You can replace the last 4 instructions to


get the sequence LLLLS.
Example: Robot Moves 60

Problem-
Solving
Paradigms
How would one do the problem directly?
Greedy
Algorithms
There is an O(n2 ) by trying all subsegments but we can’t
Linear Sweep
do better if we need to try all subsegments.
Binary Search

Okay, well we can try binary searching now. How?

Key Observation: If we can redirect the robot correctly


by replacing m instructions, then we can also do so by
replacing m + 1 instructions. Why?

Let’s turn this into a decision problem. f(m) is true if …?


Example: Robot Moves 61

Problem-
Solving
Paradigms

f(m) is true if we can correctly redirect the robot by


Greedy
Algorithms replacing a subsegment of size M.
Linear Sweep

Binary Search

We need to do this in around O(n) now. How? It’s worth


considering how to do it in O(1) if I tell you exactly what
subsegment to replace.

Reduces to, given a list of n − m instructions, can I add m


more instructions to get the robot to position x.
Example: Robot Moves 62

Problem-
Solving
Paradigms Key Observation: In m instructions, the robot can move
to every square within distance m.
Greedy
Algorithms So we are reduced to finding if there is a subsegment of
Linear Sweep size m such that its removal leaves the robot within
Binary Search distance m of x.

Now we just need to find where the robot is after the


removal of each subsegment of size m.

For this, we precompute prefix and suffix sums, where L is


−1, S is 0 and R is 1.

Then the position of the robot after removing the segment


[i, i + m) is sum[0,..,i-1] (a prefix) plus
sum[i+m,..,n-1] (a suffix).
Example: Robot Moves 63

Problem-
Solving
Paradigms #include <iostream >
using namespace std;

const int N = 200200;


Greedy int n, x;
Algorithms string moves;
// delta [i] = -1 if L, 0 if S, 1 if R
Linear Sweep // pre[i] = sum of first i moves
// suf[i] = sum of last i moves
Binary Search
int delta[N], pre[N], suf[N];

void precomp () {
for (int i = 0; i < n; i++) {
if (moves[i] == 'L')
delta[i] = -1;
if (moves[i] == 'S')
delta[i] = 0;
if (moves[i] == 'R')
delta[i] = 1;
}

for (int i = 1; i <= n; i++)


pre[i] = pre[i -1] + a[i -1];
for (int i = 1; i <= n; i++)
suf[i] = suf[i -1] + a[n-i];
}
Example: Robot Moves 64

Problem- bool f (int m) {


Solving for (int i = 0; i+m <= n; i++) {
Paradigms // try replacing [i, i+m)
int posAfterCut = pre[i] + suf[n-(i+m)];
if ( posAfterCut >= x-m && posAfterCut <= x+m)
return true;
Greedy
}
Algorithms
return false;
Linear Sweep }

Binary Search int main () {


cin >> n >> x;
cin >> moves;
precomp ();
int lo = 0;
int hi = n;
int ans = -1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
// Trying to find the lowest value that is feasible :
if (f(mid)) {
ans = mid;
hi = mid - 1;
} else {
lo = mid + 1;
}
}
cout << ans << '\n';
return 0;
}
Example: Robot Moves 65

Problem-
Solving Complexity: O(n log n).
Paradigms
Hopefully you can see the similarities between this
Greedy example and the earlier example.
Algorithms

Linear Sweep
Again, we started with a problem where approaching it
Binary Search directly was too slow.
But the problem naturally could be rephrased as finding
the minimum m such that a decision problem f(m) was
true.
So from that point onwards we only consider the decision
problem.
This still required some work but was more direct. The
idea of trying all subsegments of length m is relatively
straightforward. From that point on it was just trying to
optimize this problem with data structures.

You might also like