Some fixes
This commit is contained in:
parent
074134ac54
commit
98fda0b259
|
@ -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:
|
||||
|
|
|
@ -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)$ \\
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue