Some fixes

This commit is contained in:
Antti H S Laaksonen 2017-02-27 21:29:32 +02:00
parent 074134ac54
commit 98fda0b259
8 changed files with 54 additions and 48 deletions

View File

@ -849,7 +849,7 @@ $\lfloor \log_2(123)+1 \rfloor = 7$.
\subsubsection{IOI}
The International Olympiad in Informatics (IOI) \cite{ioi}
The International Olympiad in Informatics (IOI)
is an annual programming contest for
secondary school students.
Each country is allowed to send a team of
@ -951,7 +951,7 @@ whereas the last book contains advanced material.
Of course, general algorithm books are also suitable for
competitive programmers.
Some good books are:
Some popular books are:
\begin{itemize}
\item T. H. Cormen, C. E. Leiserson, R. L. Rivest and C. Stein:

View File

@ -313,7 +313,7 @@ assuming a time limit of one second.
\begin{center}
\begin{tabular}{ll}
typical input size & required time complexity \\
input size & required time complexity \\
\hline
$n \le 10$ & $O(n!)$ \\
$n \le 20$ & $O(2^n)$ \\

View File

@ -664,7 +664,7 @@ string s = "monkey";
sort(s.begin(), s.end());
\end{lstlisting}
Sorting a string means that the characters
in the string are sorted.
of the string are sorted.
For example, the string ''monkey'' becomes ''ekmnoy''.
\subsubsection{Comparison operators}
@ -775,7 +775,7 @@ an element $x$ in the array \texttt{t}:
\begin{lstlisting}
for (int i = 1; i <= n; i++) {
if (t[i] == x) // x found at index i
if (t[i] == x) {} // x found at index i
}
\end{lstlisting}
@ -816,7 +816,7 @@ The above idea can be implemented as follows:
int a = 1, b = n;
while (a <= b) {
int k = (a+b)/2;
if (t[k] == x) // x found at index k
if (t[k] == x) {} // x found at index k
if (t[k] > x) b = k-1;
else a = k+1;
}
@ -850,7 +850,7 @@ int k = 1;
for (int b = n/2; b >= 1; b /= 2) {
while (k+b <= n && t[k+b] <= x) k += b;
}
if (t[k] == x) // x was found at index k
if (t[k] == x) {} // x was found at index k
\end{lstlisting}
The variables $k$ and $b$ contain the position
@ -893,7 +893,7 @@ $\texttt{ok}(x)$ & \texttt{false} & \texttt{false}
\end{center}
\noindent
Now, the value $k$ can be found using binary search:
Now, the value of $k$ can be found using binary search:
\begin{lstlisting}
int x = -1;
@ -923,7 +923,7 @@ the total time complexity is $O(n \log z)$.
Binary search can also be used to find
the maximum value for a function that is
first increasing and then decreasing.
Our task is to find a value $k$ such that
Our task is to find a position $k$ such that
\begin{itemize}
\item

View File

@ -141,7 +141,7 @@ cout << c << "\n"; // tiva
A \key{set} is a data structure that
maintains a collection of elements.
The basic operations in a set are element
The basic operations of sets are element
insertion, search and removal.
C++ contains two set implementations:
@ -149,7 +149,7 @@ C++ contains two set implementations:
The structure \texttt{set} is based on a balanced
binary tree and the time complexity of its
operations is $O(\log n)$.
The structure \texttt{unordered\_set} uses a hash table,
The structure \texttt{unordered\_set} uses hashing,
and the time complexity of its operations is $O(1)$ on average.
The choice which set implementation to use
@ -262,7 +262,7 @@ the structure
binary tree and accessing elements
takes $O(\log n)$ time,
while the structure
\texttt{unordered\_map} uses a hash map
\texttt{unordered\_map} uses hashing
and accessing elements takes $O(1)$ time on average.
The following code creates a map
@ -383,7 +383,7 @@ A shorter way to write the code is as follows:
auto it = s.begin();
\end{lstlisting}
The element to which an iterator points
can be accessed through the \texttt{*} symbol.
can be accessed using the \texttt{*} symbol.
For example, the following code prints
the first element in the set:
@ -530,9 +530,9 @@ cout << (a^b) << "\n"; // 1001101110
A \texttt{deque} is a dynamic array
whose size can be changed at both ends of the array.
Like a vector, a deque contains the functions
Like a vector, a deque provides the functions
\texttt{push\_back} and \texttt{pop\_back}, but
it also contains the functions
it also provides the functions
\texttt{push\_front} and \texttt{pop\_front}
that are not available in a vector.
@ -610,7 +610,7 @@ either the minimum or maximum element.
The time complexity is $O(\log n)$
for insertion and removal and $O(1)$ for retrieval.
While a set structure efficiently supports
While an ordered set efficiently supports
all the operations of a priority queue,
the benefit in using a priority queue is
that it has smaller constant factors.

View File

@ -22,6 +22,9 @@ dynamic programming, may be needed.
We first consider the problem of generating
all subsets of a set of $n$ elements.
For example, the subsets of $\{1,2,3\}$ are
$\emptyset$, $\{1\}$, $\{2\}$, $\{3\}$, $\{1,2\}$,
$\{1,3\}$, $\{2,3\}$ and $\{1,2,3\}$.
There are two common methods for this:
we can either implement a recursive search
or use bit operations of integers.
@ -33,7 +36,7 @@ of a set is to use recursion.
The following function
generates the subsets of the set
$\{1,2,\ldots,n\}$.
The function maintains a vector
The function maintains a vector \texttt{v}
that will contain the elements of each subset.
The search begins when the function is called
with parameter 1.
@ -164,6 +167,9 @@ for (int b = 0; b < (1<<n); b++) {
Next we will consider the problem of generating
all permutations of a set of $n$ elements.
For example, the permutations of $\{1,2,3\}$ are
$(1,2,3)$, $(1,3,2)$, $(2,1,3)$, $(2,3,1)$,
$(3,1,2)$ and $(3,2,1)$.
Again, there are two approaches:
we can either use recursion or go through the
permutations iteratively.
@ -174,7 +180,7 @@ Like subsets, permutations can be generated
using recursion.
The following function goes
through the permutations of the set $\{1,2,\ldots,n\}$.
The function builds a vector that contains
The function builds a vector \texttt{v} that contains
the elements in the permutation,
and the search begins when the function is
called without parameters.
@ -351,9 +357,9 @@ void search(int y) {
\end{lstlisting}
\end{samepage}
The search begins by calling \texttt{search(0)}.
The size of the board is in the variable $n$,
The size of the board is $n$,
and the code calculates the number of solutions
to the variable $c$.
to $c$.
The code assumes that the rows and columns
of the board are numbered from 0.
@ -437,9 +443,9 @@ the $4 \times 4$ board are numbered as follows:
\end{center}
Let $q(n)$ denote the number of ways
to place $n$ queens to te $n \times n$ chessboard.
to place $n$ queens to an $n \times n$ chessboard.
The above backtracking
algorithm tells us that $q(n)=92$.
algorithm tells us that, for example, $q(8)=92$.
When $n$ increases, the search quickly becomes slow,
because the number of the solutions increases
exponentially.
@ -460,7 +466,7 @@ to a complete solution.
Such optimizations can have a tremendous
effect on the efficiency of the search.
Let us consider a problem
Let us consider the problem
of calculating the number of paths
in an $n \times n$ grid from the upper-left corner
to the lower-right corner so that each square
@ -662,7 +668,7 @@ recursive calls: 69 millions
\end{itemize}
~\\
Now it is a good moment to stop optimizing
Now is a good moment to stop optimizing
the algorithm and see what we have achieved.
The running time of the original algorithm
was 483 seconds,
@ -710,7 +716,7 @@ we can choose the numbers $[2,4,9]$ to get $2+4+9=15$.
However, if $x=10$,
it is not possible to form the sum.
A standard solution for the problem is to
An easy solution to the problem is to
go through all subsets of the elements and
check if the sum of any of the subsets is $x$.
The running time of such a solution is $O(2^n)$,
@ -731,7 +737,7 @@ Correspondingly, the second search creates
a list $S_B$ from $B$.
After this, it suffices to check if it is possible
to choose one element from $S_A$ and another
element from $S_B$ so that their sum is $x$.
element from $S_B$ such 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.
@ -745,7 +751,7 @@ and the number $9$ from $S_B$,
which corresponds to the solution $[2,4,9]$.
The time complexity of the algorithm is $O(2^{n/2})$,
because both lists $A$ and $B$ contain $n/2$ numbers
because both lists $A$ and $B$ contain about $n/2$ numbers
and it takes $O(2^{n/2})$ time to calculate the sums of
their subsets to lists $S_A$ and $S_B$.
After this, it is possible to check in

View File

@ -251,7 +251,7 @@ This algorithm selects the following events:
It turns out that this algorithm
\emph{always} produces an optimal solution.
First, it is always an optimal choice
The reason for this is that it is always an optimal choice
to first select an event that ends
as early as possible.
After this, it is an optimal choice
@ -452,7 +452,7 @@ the average of the numbers $a_1,a_2,\ldots,a_n$.
\index{codeword}
A \key{binary code} assigns for each character
of a given string a \key{codeword} that consists of bits.
of a string a \key{codeword} that consists of bits.
We can \emph{compress} the string using the binary code
by replacing each character by the
corresponding codeword.

View File

@ -73,11 +73,11 @@ subproblems.
In the coin problem, a natural recursive
problem is as follows:
what is the smallest number of coins
required for constructing sum $x$?
required for constructing a sum $x$?
Let $f(x)$ be a function that gives the answer
to the problem, i.e., $f(x)$ is the smallest
number of coins required for constructing sum $x$.
number of coins required for constructing a sum $x$.
The values of the function depend on the
values of the coins.
For example, if the coin values are $\{1,3,4\}$,
@ -142,8 +142,8 @@ we can directly implement a solution in C++:
\begin{lstlisting}
int f(int x) {
if (x == 0) return 0;
if (x < 0) return 1e9;
if (x == 0) return 0;
int u = 1e9;
for (int i = 1; i <= k; i++) {
u = min(u, f(x-c[i])+1);
@ -154,7 +154,7 @@ int f(int x) {
The code assumes that the available coins are
$\texttt{c}[1], \texttt{c}[2], \ldots, \texttt{c}[k]$,
and the value $10^9$ denotes infinity.
and $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.
@ -191,8 +191,8 @@ implemented as follows:
\begin{lstlisting}
int f(int x) {
if (x == 0) return 0;
if (x < 0) return 1e9;
if (x == 0) return 0;
if (d[x]) return d[x];
int u = 1e9;
for (int i = 1; i <= k; i++) {
@ -204,9 +204,9 @@ int f(int x) {
\end{lstlisting}
The function handles the base cases
$x=0$ and $x<0$ as previously.
$x<0$ and $x=0$ as previously.
Then the function checks if
$f(x)$ has already been calculated
$f(x)$ has already been stored
in $\texttt{d}[x]$.
If the value of $f(x)$ is found in the array,
the function directly returns it.
@ -260,7 +260,7 @@ should show how to select the coins that produce
the sum $x$ using as few coins as possible.
We can construct the solution by adding another
array to the code. The array indicates for
array to the code. The new array indicates for
each sum of money the first coin that should be
chosen in an optimal solution.
In the following code, the array \texttt{e}
@ -320,7 +320,7 @@ but now we will calculate sums of numbers of solutions.
To solve the problem, we can define a function $f(x)$
that gives the number of ways to construct
the sum $x$ using the coins.
a sum $x$ using the coins.
For example, $f(5)=6$ when the coins are $\{1,3,4\}$.
The value of $f(x)$ can be calculated recursively
using the formula
@ -709,7 +709,7 @@ depends on the values of the objects.
\index{Levenshtein distance}
The \key{edit distance} or \key{Levenshtein distance}\footnote{The distance
is named after V. I. Levenshtein who discussed it in connection with binary codes \cite{lev66}.}
is named after V. I. Levenshtein who studied it in connection with binary codes \cite{lev66}.}
is the minimum number of editing operations
needed to transform a string
into another string.
@ -942,7 +942,7 @@ $\sqsubset \sqsupset \sqsubset \sqsupset \sqsubset \sqsupset \sqcup$
Let $f(k,x)$ denote the number of ways to
construct a solution for rows $1 \ldots k$
in the grid so that string $x$ corresponds to row $k$.
It is possible to use dynamic programing here,
It is possible to use dynamic programming here,
because the state of a row is constrained
only by the state of the previous row.
@ -971,7 +971,7 @@ 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.
by using a more compact 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.

View File

@ -6,7 +6,7 @@ The time complexity of an algorithm
is often easy to analyze
just by examining the structure
of the algorithm:
what loops does the algorithm contain,
what loops does the algorithm contain
and how many times the loops are performed.
However, sometimes a straightforward analysis
does not give a true picture of the efficiency of the algorithm.
@ -95,8 +95,8 @@ contains a subarray whose sum is 8:
It turns out that the problem can be solved in
$O(n)$ time by using the two pointers method.
The idea is that
the left and right pointer indicate the
The idea is to use
left and right pointers that indicate the
first and last element of an subarray.
On each turn, the left pointer moves one step
forward, and the right pointer moves forward
@ -235,7 +235,7 @@ where the sum of the elements is $x$.
The time complexity of the algorithm depends on
the number of steps the right pointer moves.
There is no upper bound how many steps the
There is no useful upper bound how many steps the
pointer can move on a single turn.
However, the pointer moves \emph{a total of}
$O(n)$ steps during the algorithm,
@ -252,7 +252,7 @@ the time complexity is $O(n)$.
Another problem that can be solved using
the two pointers method is the following problem,
also known as the \key{2SUM problem}:
We are given an array of $n$ numbers and
we are given an array of $n$ numbers and
a target sum $x$, and our task is to find two numbers
in the array such that their sum is $x$,
or report that no such numbers exist.
@ -452,13 +452,13 @@ It turns out that the problem can be solved
in $O(n)$ time using an appropriate data structure.
An efficient solution to the problem is to
iterate through the array from left to right,
iterate through the array from left to right
and maintain a chain of elements where the
first element is the current element
and each following element is the nearest smaller
element of the previous element.
If the chain only contains one element,
the current element does not have the nearest smaller element.
the current element does not have a nearest smaller element.
At each step, elements are removed from the chain
until the first element is smaller
than the current element, or the chain is empty.