Corrections

This commit is contained in:
Antti H S Laaksonen 2017-02-13 23:16:30 +02:00
parent 3dd874a4fa
commit 3f31020076
3 changed files with 87 additions and 86 deletions

View File

@ -2,7 +2,7 @@
\key{Complete search}
is a general method that can be used
for solving almost any algorithm problem.
to solve almost any algorithm problem.
The idea is to generate all possible
solutions to the problem using brute force,
and then select the best solution or count the
@ -30,7 +30,7 @@ or use bit operations of integers.
An elegant way to go through all subsets
of a set is to use recursion.
The following function \texttt{gen}
The following function
generates the subsets of the set
$\{1,2,\ldots,n\}$.
The function maintains a vector
@ -128,8 +128,8 @@ which corresponds to an integer between $0 \ldots 2^n-1$.
The ones in the bit sequence indicate
which elements are included in the subset.
The usual convention is that element $k$
is included in the subset if the $k$th last bit
The usual convention is that the $k$th element
is included in the subset exactly when the $k$th last bit
in the sequence is one.
For example, the bit representation of 25
is 11001, that corresponds to the subset $\{1,4,5\}$.
@ -143,8 +143,8 @@ for (int b = 0; b < (1<<n); b++) {
}
\end{lstlisting}
The following code shows how we can derive
the elements in a subset from the bit sequence.
The following code shows how we can find
the elements of a subset that corresponds to a bit sequence.
When processing each subset,
the code builds a vector that contains the
elements in the subset.
@ -165,14 +165,14 @@ for (int b = 0; b < (1<<n); b++) {
Next we will consider the problem of generating
all permutations of a set of $n$ elements.
Again, there are two approaches:
we can either use recursion or go trough the
we can either use recursion or go through the
permutations iteratively.
\subsubsection{Method 1}
Like subsets, permutations can be generated
using recursion.
The following function \texttt{gen} goes
The following function goes
through the permutations of the set $\{1,2,\ldots,n\}$.
The function builds a vector that contains
the elements in the permutation,
@ -180,7 +180,7 @@ and the search begins when the function is
called without parameters.
\begin{lstlisting}
void haku() {
void gen() {
if (v.size() == n) {
// process permutation v
} else {
@ -188,7 +188,7 @@ void haku() {
if (p[i]) continue;
p[i] = 1;
v.push_back(i);
haku();
gen();
p[i] = 0;
v.pop_back();
}
@ -275,8 +275,9 @@ any of the queens placed before.
A solution has been found when all
$n$ queens have been placed to the board.
For example, when $n=4$, the backtracking
algorithm generates the following tree:
For example, when $n=4$,
some partial solutions generated by
the backtracking algorithm are as follows:
\begin{center}
\begin{tikzpicture}[scale=.55]
@ -436,7 +437,7 @@ the $4 \times 4$ board are numbered as follows:
\end{center}
The above backtracking
algorithm shows that
algorithm tells us that
there are 92 ways to place 8
queens to the $8 \times 8$ chessboard.
When $n$ increases, the search quickly becomes slow,
@ -453,7 +454,7 @@ on a modern computer
We can often optimize backtracking
by pruning the search tree.
The idea is to add ''intelligence'' to the algorithm
so that it will realize as soon as possible
so that it will notice as soon as possible
if a partial solution cannot be extended
to a complete solution.
Such optimizations can have a tremendous
@ -484,7 +485,8 @@ One of the paths is as follows:
\end{tikzpicture}
\end{center}
Next we will concentrate on the $7 \times 7$ case.
We will concentrate on the $7 \times 7$ case,
because its level of difficulty is appropriate to our needs.
We begin with a straightforward backtracking algorithm,
and then optimize it step by step using observations
how the search can be pruned.
@ -509,7 +511,7 @@ recursive calls: 76 billions
\subsubsection{Optimization 1}
In any solution, we first move a step
In any solution, we first move one step
down or right.
There are always two paths that
are symmetric
@ -551,7 +553,7 @@ For example, the following paths are symmetric:
\end{center}
Hence, we can decide that we always first
move down,
move one step down,
and finally multiply the number of the solutions by two.
\begin{itemize}
@ -683,9 +685,9 @@ i.e., at the top of the search tree.
\key{Meet in the middle} is a technique
where the search space is divided into
two equally large parts.
two parts of about equal size.
A separate search is performed
for each of the parts,
for both of the parts,
and finally the results of the searches are combined.
The technique can be used
@ -727,8 +729,8 @@ to a list $S_A$.
Correspondingly, the second search creates
a list $S_B$ from $B$.
After this, it suffices to check if it is possible
to choose one number from $S_A$ and another
number from $S_B$ so that their sum is $x$.
to choose one element from $S_A$ and another
element from $S_B$ so that their sum is $x$.
This is possible exactly when there is a way to
form the sum $x$ using the numbers in the original list.
@ -736,7 +738,7 @@ For example, suppose that the list is $[2,4,5,9]$ and $x=15$.
First, we divide the list into $A=[2,4]$ and $B=[5,9]$.
After this, we create lists
$S_A=[0,2,4,6]$ and $S_B=[0,5,9,14]$.
In this case, the sum $x=15$ is possible to form
In this case, the sum $x=15$ is possible to form,
because we can choose the number $6$ from $S_A$
and the number $9$ from $S_B$,
which corresponds to the solution $[2,4,9]$.

View File

@ -12,8 +12,8 @@ the final solution.
For this reason, greedy algorithms
are usually very efficient.
The difficulty in designing a greedy algorithm
is to invent a greedy strategy
The difficulty in designing greedy algorithms
is to find a greedy strategy
that always produces an optimal solution
to the problem.
The locally optimal choices in a greedy
@ -32,7 +32,7 @@ $\{c_1,c_2,\ldots,c_k\}$,
and each coin can be used as many times we want.
What is the minimum number of coins needed?
For example, if the coins are euro coins (in cents)
For example, if the coins are the euro coins (in cents)
\[\{1,2,5,10,20,50,100,200\}\]
and the sum of money is 520,
we need at least four coins.
@ -71,13 +71,13 @@ because we could replace
coins $2+2+2$ by coins $5+1$ and
coins $20+20+20$ by coins $50+10$.
Moreover, an optimal solution cannot contain
coins $2+2+1$ or $20+20+10$
coins $2+2+1$ or $20+20+10$,
because we could replace them by coins $5$ and $50$.
Using these observations,
we can show for each coin $x$ that
it is not possible to optimally construct
sum $x$ or any larger sum by only using coins
a sum $x$ or any larger sum by only using coins
that are smaller than $x$.
For example, if $x=100$, the largest optimal
sum using the smaller coins is $50+20+20+5+2+2=99$.
@ -113,10 +113,10 @@ correct answer.
\section{Scheduling}
Many scheduling problems can be solved
using a greedy strategy.
using greedy algorithms.
A classic problem is as follows:
Given $n$ events with their starting and ending
times, we should plan a schedule
times, our goal is to plan a schedule
that includes as many events as possible.
It is not possible to select an event partially.
For example, consider the following events:
@ -172,7 +172,7 @@ selects the following events:
\end{tikzpicture}
\end{center}
However, select short events is not always
However, selecting short events is not always
a correct strategy, but the algorithm fails,
for example, in the following case:
\begin{center}
@ -269,9 +269,9 @@ and the greedy algorithm is correct.
\section{Tasks and deadlines}
Let us now consider a problem where
we are given $n$ tasks with durations and deadlines,
we are given $n$ tasks with durations and deadlines
and our task is to choose an order to perform the tasks.
For each task, we get $d-x$ points
For each task, we earn $d-x$ points
where $d$ is the task's deadline
and $x$ is the moment when we finished the task.
What is the largest possible total score
@ -367,7 +367,7 @@ Here $a>b$, so we should swap the tasks:
\end{scope}
\end{tikzpicture}
\end{center}
Now $X$ gives $b$ points less and $Y$ gives $a$ points more,
Now $X$ gives $b$ points fewer and $Y$ gives $a$ points more,
so the total score increases by $a-b > 0$.
In an optimal solution,
for any two consecutive tasks,
@ -470,7 +470,7 @@ which means that the length of each
codeword is the same.
For example, we can compress the string
\texttt{AABACDACA} as follows:
\[000001001011001000\]
\[00\,00\,01\,00\,10\,11\,00\,10\,00\]
Using this code, the length of the compressed
string is 18 bits.
However, we can compress the string better
@ -496,7 +496,7 @@ An optimal code produces a compressed string
that is as short as possible.
In this case, the compressed string using
the optimal code is
\[001100101110100,\]
\[0\,0\,110\,0\,10\,111\,0\,10\,0,\]
so only 15 bits are needed instead of 18 bits.
Thus, thanks to a better code it was possible to
save 3 bits in the compressed string.
@ -539,12 +539,12 @@ in the string,
and each character's codeword can be read
by following a path from the root to
the corresponding node.
A move to the left correspons to bit 0,
A move to the left corresponds to bit 0,
and a move to the right corresponds to bit 1.
Initially, each character of the string is
represented by a node whose weight is the
number of times the character appears in the string.
number of times the character occurs in the string.
Then at each step two nodes with minimum weights
are combined by creating
a new node whose weight is the sum of the weights

View File

@ -14,7 +14,7 @@ There are two uses for dynamic programming:
\begin{itemize}
\item
\key{Findind an optimal solution}:
\key{Finding an optimal solution}:
We want to find a solution that is
as large as possible or as small as possible.
\item
@ -24,14 +24,14 @@ possible solutions.
\end{itemize}
We will first see how dynamic programming can
be used for finding an optimal solution,
be used to find an optimal solution,
and then we will use the same idea for
counting the solutions.
Understanding dynamic programming is a milestone
in every competitive programmer's career.
While the basic idea of the technique is simple,
the challenge is how to apply it for different problems.
the challenge is how to apply it to different problems.
This chapter introduces a set of classic problems
that are a good starting point.
@ -47,7 +47,7 @@ In Chapter 6, we solved the problem using a
greedy algorithm that always selects the largest
possible coin.
The greedy algorithm works, for example,
when coins are euro coins,
when the coins are the euro coins,
but in the general case the greedy algorithm
does not necessarily produce an optimal solution.
@ -60,15 +60,15 @@ that goes through all possibilities how to
form the sum, like a brute force algorithm.
However, the dynamic programming
algorithm is efficient because
it uses \emph{memoization} to
calculate the answer to each subproblem only once.
it uses \emph{memoization} and
calculates the answer to each subproblem only once.
\subsubsection{Recursive formulation}
The idea in dynamic programming is to
formulate the problem recursively so
that the answer to the problem can be
calculated from the answers for smaller
calculated from answers to smaller
subproblems.
In the coin problem, a natural recursive
problem is as follows:
@ -107,8 +107,8 @@ and $f(5)=2$ because the sum 5 can
be formed using coins 1 and 4.
The essential property in the function is
that the value $f(x)$ can be calculated
recursively from the smaller values of the function.
that each value of $f(x)$ can be calculated
recursively from smaller values of the function.
For example, if the coin set is $\{1,3,4\}$,
there are three ways to select the first coin
in a solution: we can choose coin 1, 3 or 4.
@ -133,9 +133,8 @@ In addition, it is convenient to define
\[f(x)=\infty\hspace{8px}\textrm{if $x<0$}.\]
This means that an infinite number of coins
is needed for forming a negative sum of money.
This prevents the situation that the recursive
function would form a solution where the
initial sum of money is negative.
This prevents the function from constructing
a solution where the initial sum of money is negative.
Once a recursive function that solves the problem
has been found,
@ -159,7 +158,7 @@ and the value $10^9$ denotes infinity.
This function works but it is not efficient yet,
because it goes through a large number
of ways to construct the sum.
However, the function becomes efficient by
However, the function can be made efficient by
using memoization.
\subsubsection{Memoization}
@ -170,20 +169,20 @@ Dynamic programming allows us to calculate the
value of a recursive function efficiently
using \key{memoization}.
This means that an auxiliary array is used
for storing the values of the function
for recording the values of the function
for different parameters.
For each parameter, the value of the function
is calculated recursively only once, and after this,
the value can be directly retrieved from the array.
In this problem, we can use the array
In this problem, we can use an array
\begin{lstlisting}
int d[N];
\end{lstlisting}
where $\texttt{d}[x]$ will contain
the value $f(x)$.
The constant $N$ should be chosen so
the value of $f(x)$.
The constant $N$ has to be chosen so
that all required values of the function fit
in the array.
@ -208,23 +207,23 @@ The function handles the base cases
$x=0$ and $x<0$ as previously.
Then the function checks if
$f(x)$ has already been calculated
and stored in $\texttt{d}[x]$.
If $f(x)$ is found in the array,
in $\texttt{d}[x]$.
If the value of $f(x)$ is found in the array,
the function directly returns it.
Otherwise the function calculates the value
recursively and stores it in $\texttt{d}[x]$.
Using memoization the function works
efficiently, because the answer for each $x$
efficiently, because the answer for each parameter $x$
is calculated recursively only once.
After a value of $f(x)$ has been stored in the array,
it can be efficiently retrieved whenever the
function will be called again with parameter $x$.
function will be called again with the parameter $x$.
The time complexity of the resulting algorithm
is $O(xk)$ where the sum is $x$ and the number of
coins is $k$.
In practice, the algorithm is usable if
In practice, the algorithm can be used if
$x$ is so small that it is possible to allocate
an array for all possible function parameters.
@ -294,7 +293,7 @@ while (x > 0) {
\subsubsection{Counting the number of solutions}
Let us now consider a variation of the problem
Let us now consider a variant of the problem
that is otherwise like the original problem,
but we should count the total number of solutions instead
of finding the optimal solution.
@ -319,7 +318,7 @@ The difference is that when finding the optimal solution,
we maximize or minimize something in the recursion,
but now we will calculate sums of numbers of solutions.
In the coin problem, we can define a function $f(x)$
To solve the problem, we can define a function $f(x)$
that returns the number of ways to construct
the sum $x$ using the coins.
For example, $f(5)=6$ when the coins are $\{1,3,4\}$.
@ -330,7 +329,7 @@ because to form the sum $x$, we have to first
choose some coin $c_i$ and then form the sum $x-c_i$.
The base cases are $f(0)=1$, because there is exactly
one way to form the sum 0 using an empty set of coins,
and $f(x)=0$, when $x<0$, because it's not possible
and $f(x)=0$, when $x<0$, because it is not possible
to form a negative sum of money.
If the coin set is $\{1,3,4\}$, the function is
@ -370,8 +369,8 @@ that it is not required to calculate the exact number
but it is enough to give the answer modulo $m$
where, for example, $m=10^9+7$.
This can be done by changing the code so that
all calculations are done in modulo $m$.
In the above code, it is enough to add the line
all calculations are done modulo $m$.
In the above code, it suffices to add the line
\begin{lstlisting}
d[i] %= m;
\end{lstlisting}
@ -380,7 +379,7 @@ after the line
d[i] += d[i-c[j]];
\end{lstlisting}
Now we have covered all basic
Now we have discussed all basic
techniques related to
dynamic programming.
Since dynamic programming can be used
@ -397,7 +396,7 @@ Given an array that contains $n$
numbers $x_1,x_2,\ldots,x_n$,
our task is to find the
\key{longest increasing subsequence}
in the array.
of the array.
This is a sequence of array elements
that goes from left to right,
and each element in the sequence is larger
@ -496,8 +495,8 @@ where $i<k$ and $x_i<x_k$. In this case $f(k)=f(i)+1$.
For example, in the above example $f(7)=4$,
because the subsequence $[2,5,7]$ of length 3
ends at position 5, and after adding the element
at position 7 to the subsequence,
ends at position 5, and by adding the element
at position 7 to this subsequence,
we get the optimal subsequence $[2,5,7,8]$ of length 4.
An easy way to calculate the
@ -514,7 +513,7 @@ Our next problem is to find a path
in an $n \times n$ grid
from the upper-left corner to
the lower-right corner such that
we can only move down and right.
we only move down and right.
Each square contains a number,
and the path should be constructed so
that the sum of numbers along
@ -570,17 +569,17 @@ upper-left corner to the lower-right corner.
We can approach the problem by
calculating for each square $(y,x)$
the largest possible sum on a path
the maximum sum on a path
from the upper-left corner to square $(y,x)$.
Let $f(y,x)$ denote this sum,
so $f(n,n)$ is the largest sum on a path
so $f(n,n)$ is the maximum sum on a path
from the upper-left corner to
the lower-right corner.
The recursive formula is based on the observation
that a path that ends at square $(y,x)$
can either come from square $(y,x-1)$
or from square $(y-1,x)$:
can come either from square $(y,x-1)$
or square $(y-1,x)$:
\begin{center}
\begin{tikzpicture}[scale=.65]
\begin{scope}
@ -682,7 +681,7 @@ denote the smallest possible total weight
when a subset of objects
$1 \ldots k$ is selected such
that the total weight is $u$.
The solution for the problem is the
The solution to the problem is the
largest value $u$
for which $0 \le u \le s$ and $f(n,u) \le x$
where $s=\sum_{i=1}^n a_i$.
@ -711,8 +710,8 @@ depends on the values of the objects.
The \key{edit distance} or \key{Levenshtein distance}
is the minimum number of editing operations
needed for transforming the first string
into the second string.
needed to transform a string
into another string.
The allowed editing operations are as follows:
\begin{itemize}
\item insert a character (e.g. \texttt{ABC} $\rightarrow$ \texttt{ABCA})
@ -721,10 +720,10 @@ The allowed editing operations are as follows:
\end{itemize}
For example, the edit distance between
\texttt{LOVE} and \texttt{MOVIE} is 2
because we can first perform operation
\texttt{LOVE} and \texttt{MOVIE} is 2,
because we can first perform the operation
\texttt{LOVE} $\rightarrow$ \texttt{MOVE}
(change) and then operation
(change) and then the operation
\texttt{MOVE} $\rightarrow$ \texttt{MOVIE}
(insertion).
This is the smallest possible number of operations,
@ -736,7 +735,7 @@ Suppose we are given strings
$n$ and $m$ characters, respectively,
and we wish to calculate the edit distance
between them.
This can be efficiently done using
This can be done using
dynamic programming in $O(nm)$ time.
Let $f(a,b)$ denote the edit distance
between the first $a$ characters of \texttt{x}
@ -756,7 +755,7 @@ and in the general case the formula is
where $c=0$ if the $a$th character of \texttt{x}
equals the $b$th character of \texttt{y},
and otherwise $c=1$.
The formula considers all ways how to shorten the strings:
The formula considers all possible ways to shorten the strings:
\begin{itemize}
\item $f(a,b-1)$ means that a character is inserted to \texttt{x}
\item $f(a-1,b)$ means that a character is removed from \texttt{x}
@ -819,7 +818,7 @@ in the example case:
\end{center}
The lower-right corner of the table
indicates that the edit distance between
tells us that the edit distance between
\texttt{LOVE} and \texttt{MOVIE} is 2.
The table also shows how to construct
the shortest sequence of editing operations.
@ -889,7 +888,7 @@ the edit distance between \texttt{LOV} and \texttt{MOV}, etc.
\section{Counting tilings}
Sometimes the states in a dynamic programming solution
Sometimes the states of a dynamic programming solution
are more complex than fixed combinations of numbers.
As an example,
we consider the problem of calculating
@ -971,9 +970,9 @@ so that the shorter side has length $m$,
because the factor $4^{2m}$ dominates the time complexity.
It is possible to make the solution more efficient
by using a better representation for the rows as strings.
It turns out that it is sufficient to know the
columns of the previous row that contain the first square
by using a better representation for the rows.
It turns out that it is sufficient to know which
columns of the previous row contain the upper square
of a vertical tile.
Thus, we can represent a row using only characters
$\sqcap$ and $\Box$, where $\Box$ is a combination