Corrections

This commit is contained in:
Antti H S Laaksonen 2017-02-01 00:08:52 +02:00
parent e42cfb413e
commit 39e2981355
1 changed files with 133 additions and 138 deletions

View File

@ -6,9 +6,9 @@
is a technique that combines the correctness is a technique that combines the correctness
of complete search and the efficiency of complete search and the efficiency
of greedy algorithms. of greedy algorithms.
Dynamic programming can be used if the Dynamic programming can be applied if the
problem can be divided into subproblems problem can be divided into overlapping subproblems
that can be calculated independently. that can be solved independently.
There are two uses for dynamic programming: There are two uses for dynamic programming:
@ -18,7 +18,7 @@ There are two uses for dynamic programming:
We want to find a solution that is We want to find a solution that is
as large as possible or as small as possible. as large as possible or as small as possible.
\item \item
\key{Couting the number of solutions}: \key{Counting the number of solutions}:
We want to calculate the total number of We want to calculate the total number of
possible solutions. possible solutions.
\end{itemize} \end{itemize}
@ -37,49 +37,50 @@ that are a good starting point.
\section{Coin problem} \section{Coin problem}
We first consider a problem that we We first discuss a problem that we
have already seen: have already seen in Chapter 6:
Given a set of coin values $\{c_1,c_2,\ldots,c_k\}$ Given a set of coin values $\{c_1,c_2,\ldots,c_k\}$
and a sum of money $x$, our task is to and a sum of money $x$, our task is to
form the sum $x$ using as few coins as possible. form the sum $x$ using as few coins as possible.
In Chapter 6.1, we solved the problem using a In Chapter 6, we solved the problem using a
greedy algorithm that always selects the largest greedy algorithm that always selects the largest
possible coin for the sum. possible coin.
The greedy algorithm works, for example, The greedy algorithm works, for example,
when the coins are the euro coins, when coins are euro coins,
but in the general case the greedy algorithm but in the general case the greedy algorithm
doesn't necessarily produce an optimal solution. does not necessarily produce an optimal solution.
Now it's time to solve the problem efficiently Now it is time to solve the problem efficiently
using dynamic programming, so that the algorithms using dynamic programming, so that the algorithm
works for any coin set. works for any coin set.
The dynamic programming The dynamic programming
algorithm is based on a recursive function algorithm is based on a recursive function
that goes through all possibilities how to that goes through all possibilities how to
select the coins, like a brute force algorithm. form the sum, like a brute force algorithm.
However, the dynamic programming However, the dynamic programming
algorithm is efficient because algorithm is efficient because
it uses memoization to it uses \emph{memoization} to
calculate the answer for each subproblem only once. calculate the answer to each subproblem only once.
\subsubsection{Recursive formulation} \subsubsection{Recursive formulation}
The idea in dynamic programming is to The idea in dynamic programming is to
formulate the problem recursively so formulate the problem recursively so
that the answer for the problem can be that the answer to the problem can be
calculated from the answers for the smaller calculated from the answers for smaller
subproblems. subproblems.
In this case, a natural problem is as follows: In the coin problem, a natural recursive
problem is as follows:
what is the smallest number of coins what is the smallest number of coins
required for constructing sum $x$? required for constructing sum $x$?
Let $f(x)$ be a function that gives the answer Let $f(x)$ be a function that gives the answer
for the problem, i.e., $f(x)$ is the smallest to the problem, i.e., $f(x)$ is the smallest
number of coins required for constructing sum $x$. number of coins required for constructing sum $x$.
The values of the function depend on the The values of the function depend on the
values of the coins. values of the coins.
For example, if the values are $\{1,3,4\}$, For example, if the coin values are $\{1,3,4\}$,
the first values of the function are as follows: the first values of the function are as follows:
\[ \[
@ -99,7 +100,7 @@ f(10) & = & 3 \\
\] \]
First, $f(0)=0$ because no coins are needed First, $f(0)=0$ because no coins are needed
for sum $0$. for the sum $0$.
Moreover, $f(3)=1$ because the sum $3$ Moreover, $f(3)=1$ because the sum $3$
can be formed using coin 3, can be formed using coin 3,
and $f(5)=2$ because the sum 5 can and $f(5)=2$ because the sum 5 can
@ -128,16 +129,17 @@ The base case for the function is
\[f(0)=0,\] \[f(0)=0,\]
because no coins are needed for constructing because no coins are needed for constructing
the sum 0. the sum 0.
In addition, it's a good idea to define In addition, it is convenient to define
\[f(x)=\infty,\hspace{8px}\textrm{jos $x<0$}.\] \[f(x)=\infty\hspace{8px}\textrm{if $x<0$}.\]
This means that an infinite number of coins This means that an infinite number of coins
is needed to create a negative sum of money. is needed for forming a negative sum of money.
This prevents the situation that the recursive This prevents the situation that the recursive
function would form a solution where the function would form a solution where the
initial sum of money is negative. initial sum of money is negative.
Now it's possible to implement the function in C++ Once a recursive function that solves the problem
directly using the recursive definition: has been found,
we can directly implement a solution in C++:
\begin{lstlisting} \begin{lstlisting}
int f(int x) { int f(int x) {
@ -153,8 +155,8 @@ int f(int x) {
The code assumes that the available coins are The code assumes that the available coins are
$\texttt{c}[1], \texttt{c}[2], \ldots, \texttt{c}[k]$, $\texttt{c}[1], \texttt{c}[2], \ldots, \texttt{c}[k]$,
and the value $10^9$ means infinity. and the value $10^9$ denotes infinity.
This function works but it is not efficient yet This function works but it is not efficient yet,
because it goes through a large number because it goes through a large number
of ways to construct the sum. of ways to construct the sum.
However, the function becomes efficient by However, the function becomes efficient by
@ -164,15 +166,15 @@ using memoization.
\index{memoization} \index{memoization}
Dynamic programming allows to calculate the Dynamic programming allows us to calculate the
value of a recursive function efficiently value of a recursive function efficiently
using \key{memoization}. using \key{memoization}.
This means that an auxiliary array is used This means that an auxiliary array is used
for storing the values of the function for storing the values of the function
for different parameters. for different parameters.
For each parameter, the value of the function For each parameter, the value of the function
is calculated only once, and after this, is calculated recursively only once, and after this,
it can be directly retrieved from the array. the value can be directly retrieved from the array.
In this problem, we can use the array In this problem, we can use the array
\begin{lstlisting} \begin{lstlisting}
@ -182,8 +184,8 @@ int d[N];
where $\texttt{d}[x]$ will contain where $\texttt{d}[x]$ will contain
the value $f(x)$. the value $f(x)$.
The constant $N$ should be chosen so The constant $N$ should be chosen so
that there is space for all needed that all required values of the function fit
values of the function. in the array.
After this, the function can be efficiently After this, the function can be efficiently
implemented as follows: implemented as follows:
@ -206,22 +208,21 @@ 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 Then the function checks if
$f(x)$ has already been calculated $f(x)$ has already been calculated
and stored to $\texttt{d}[x]$. and stored in $\texttt{d}[x]$.
If $f(x)$ can be found in the array, If $f(x)$ is found in the array,
the function directly returns it. the function directly returns it.
Otherwise the function calculates the value Otherwise the function calculates the value
recursively and stores it to $\texttt{d}[x]$. recursively and stores it in $\texttt{d}[x]$.
Using memoization the function works Using memoization the function works
efficiently because it is needed to efficiently, because the answer for each $x$
recursively calculate is calculated recursively only once.
the answer for each $x$ only once. After a value of $f(x)$ has been stored in the array,
After a value $f(x)$ has been stored to the array, it can be efficiently retrieved whenever the
it can be directly retrieved whenever the
function will be called again with parameter $x$. function will be called again with parameter $x$.
The time complexity of the resulting algorithm The time complexity of the resulting algorithm
is $O(xk)$ when the sum is $x$ and the number of is $O(xk)$ where the sum is $x$ and the number of
coins is $k$. coins is $k$.
In practice, the algorithm is usable if In practice, the algorithm is usable if
$x$ is so small that it is possible to allocate $x$ is so small that it is possible to allocate
@ -245,17 +246,17 @@ for (int i = 1; i <= x; i++) {
This implementation is shorter and somewhat This implementation is shorter and somewhat
more efficient than recursion, more efficient than recursion,
and experienced competitive programmers and experienced competitive programmers
often implement dynamic programming solutions often prefer dynamic programming solutions
using loops. that are implemented using loops.
Still, the underlying idea is the same as Still, the underlying idea is the same as
in the recursive function. in the recursive function.
\subsubsection{Constructing the solution} \subsubsection{Constructing the solution}
Sometimes it is not enough to find out the value Sometimes we are asked both to find the value
of the optimal solution, but we should also give of an optimal solution and also to give
an example how such a solution can be constructed. an example how such a solution can be constructed.
In this problem, this means that the algorithm In the coin problem, this means that the algorithm
should show how to select the coins that produce should show how to select the coins that produce
the sum $x$ using as few coins as possible. the sum $x$ using as few coins as possible.
@ -294,11 +295,11 @@ while (x > 0) {
\subsubsection{Counting the number of solutions} \subsubsection{Counting the number of solutions}
Let us now consider a variation of the problem Let us now consider a variation of the problem
that it's like the original problem but we should that is otherwise like the original problem,
count the total number of solutions instead but we should count the total number of solutions instead
of finding the optimal solution. of finding the optimal solution.
For example, if the coins are $\{1,3,4\}$ and For example, if the coins are $\{1,3,4\}$ and
the required sum is $5$, the target sum is $5$,
there are a total of 6 solutions: there are a total of 6 solutions:
\begin{multicols}{2} \begin{multicols}{2}
@ -316,24 +317,23 @@ The number of the solutions can be calculated
using the same idea as finding the optimal solution. using the same idea as finding the optimal solution.
The difference is that when finding the optimal solution, The difference is that when finding the optimal solution,
we maximize or minimize something in the recursion, we maximize or minimize something in the recursion,
but now we will sum together all possible alternatives to but now we will calculate sums of numbers of solutions.
construct a solution.
In this case, we can define a function $f(x)$ In the coin problem, we can define a function $f(x)$
that returns the number of ways to construct that returns the number of ways to construct
the sum $x$ using the coins. the sum $x$ using the coins.
For example, $f(5)=6$ when the coins are $\{1,3,4\}$. For example, $f(5)=6$ when the coins are $\{1,3,4\}$.
The function $f(x)$ can be recursively calculated The value of $f(x)$ can be calculated recursively
using the formula using the formula
\[ f(x) = f(x-c_1)+f(x-c_2)+\cdots+f(x-c_k)\] \[ f(x) = f(x-c_1)+f(x-c_2)+\cdots+f(x-c_k),\]
because to form the sum $x$ we should first because to form the sum $x$, we have to first
choose some coin $c_i$ and after this form the sum $x-c_i$. choose some coin $c_i$ and then form the sum $x-c_i$.
The base cases are $f(0)=1$ because there is exactly The base cases are $f(0)=1$, because there is exactly
one way to form the sum 0 using an empty set of coins, 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's not possible
to form a negative sum of money. to form a negative sum of money.
In the above example the function becomes If the coin set is $\{1,3,4\}$, the function is
\[ f(x) = f(x-1)+f(x-3)+f(x-4) \] \[ f(x) = f(x-1)+f(x-3)+f(x-4) \]
and the first values of the function are: and the first values of the function are:
\[ \[
@ -351,7 +351,7 @@ f(9) & = & 40 \\
\end{array} \end{array}
\] \]
The following code calculates the value $f(x)$ The following code calculates the value of $f(x)$
using dynamic programming by filling the array using dynamic programming by filling the array
\texttt{d} for parameters $0 \ldots x$: \texttt{d} for parameters $0 \ldots x$:
@ -365,13 +365,13 @@ for (int i = 1; i <= x; i++) {
} }
\end{lstlisting} \end{lstlisting}
Often the number of the solutions is so large Often the number of solutions is so large
that it is not required to calculate the exact number that it is not required to calculate the exact number
but it is enough to give the answer modulo $m$ but it is enough to give the answer modulo $m$
where, for example, $m=10^9+7$. where, for example, $m=10^9+7$.
This can be done by changing the code so that This can be done by changing the code so that
all calculations will be done in modulo $m$. all calculations are done in modulo $m$.
In this case, it is enough to add the line In the above code, it is enough to add the line
\begin{lstlisting} \begin{lstlisting}
d[i] %= m; d[i] %= m;
\end{lstlisting} \end{lstlisting}
@ -386,8 +386,8 @@ dynamic programming.
Since dynamic programming can be used Since dynamic programming can be used
in many different situations, in many different situations,
we will now go through a set of problems we will now go through a set of problems
that show further examples how dynamic that show further examples about
programming can be used. possibilities of dynamic programming.
\section{Longest increasing subsequence} \section{Longest increasing subsequence}
@ -395,11 +395,11 @@ programming can be used.
Given an array that contains $n$ Given an array that contains $n$
numbers $x_1,x_2,\ldots,x_n$, numbers $x_1,x_2,\ldots,x_n$,
our task is find the our task is to find the
\key{longest increasing subsequence} \key{longest increasing subsequence}
in the array. in the array.
This is a sequence of array elements This is a sequence of array elements
that goes from the left to the right, that goes from left to right,
and each element in the sequence is larger and each element in the sequence is larger
than the previous element. than the previous element.
For example, in the array For example, in the array
@ -463,12 +463,12 @@ contains 4 elements:
Let $f(k)$ be the length of the Let $f(k)$ be the length of the
longest increasing subsequence longest increasing subsequence
that ends to index $k$. that ends at position $k$.
Thus, the answer for the problem Using this function, the answer to the problem
is the largest of values is the largest of values
$f(1),f(2),\ldots,f(n)$. $f(1),f(2),\ldots,f(n)$.
For example, in the above array For example, in the above array
the values for the function are as follows: the values of the function are as follows:
\[ \[
\begin{array}{lcl} \begin{array}{lcl}
f(1) & = & 1 \\ f(1) & = & 1 \\
@ -482,30 +482,30 @@ f(8) & = & 2 \\
\end{array} \end{array}
\] \]
When calculating the value $f(k)$, When calculating the value of $f(k)$,
there are two possibilities how the subsequence there are two possibilities how the subsequence
that ends to index $k$ is constructed: that ends at position $k$ is constructed:
\begin{enumerate} \begin{enumerate}
\item The subsequence \item The subsequence
only contains the element $x_k$, so $f(k)=1$. only contains the element $x_k$, so $f(k)=1$.
\item We choose some index $i$ for which $i<k$ \item We choose some position $i$ for which $i<k$
and $x_i<x_k$. and $x_i<x_k$.
We extend the longest increasing subsequence We extend the longest increasing subsequence
that ends to index $i$ by adding the element $x_k$ that ends at position $i$ by adding the element $x_k$
to it. In this case $f(k)=f(i)+1$. to it. In this case $f(k)=f(i)+1$.
\end{enumerate} \end{enumerate}
Consider calculating the value $f(7)$. Consider calculating the value of $f(7)$.
The best solution is to extend the longest The best solution is to extend the longest
increasing subsequence that ends to index 5, increasing subsequence that ends at position 5,
i.e., the sequence $[2,5,7]$, by adding i.e., the sequence $[2,5,7]$, by adding
the element $x_7=8$. the element $x_7=8$.
The result is The result is
$[2,5,7,8]$, and $f(7)=f(5)+1=4$. $[2,5,7,8]$, and $f(7)=f(5)+1=4$.
A straightforward way to calculate the An easy way to calculate the
value $f(k)$ is to value of $f(k)$ is to
go through all indices inspect all positions
$i=1,2,\ldots,k-1$ that can contain the $i=1,2,\ldots,k-1$ that can contain the
previous element in the subsequence. previous element in the subsequence.
The time complexity of such an algorithm is $O(n^2)$. The time complexity of such an algorithm is $O(n^2)$.
@ -517,16 +517,15 @@ problem in $O(n \log n)$ time, but this is more difficult.
Our next problem is to find a path Our next problem is to find a path
in an $n \times n$ grid in an $n \times n$ grid
from the upper-left corner to from the upper-left corner to
the lower-right corner. the lower-right corner such that
we can only move down and right.
Each square contains a number, Each square contains a number,
and the path should be constructed so and the path should be constructed so
that the sum of numbers along that the sum of numbers along
the path is as large as possible. the path is as large as possible.
In addition, it is only allowed to move
downwards and to the right.
In the followig grid, the best path is The following picture shows an optimal
marked with gray background: path in a grid:
\begin{center} \begin{center}
\begin{tikzpicture}[scale=.65] \begin{tikzpicture}[scale=.65]
\begin{scope} \begin{scope}
@ -568,23 +567,22 @@ marked with gray background:
\end{scope} \end{scope}
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
The sum of numbers is The sum of numbers on the path is 67,
$3+9+8+7+9+8+5+10+8=67$ and this is the largest possible sum on a path
that is the largest possible sum in a path
from the from the
upper-left corner to the lower-right corner. upper-left corner to the lower-right corner.
A good approach for the problem is to We can approach the problem by
calculate for each square $(y,x)$ calculating for each square $(y,x)$
the largest possible sum in a path the largest possible sum on a path
from the upper-left corner to the square $(y,x)$. from the upper-left corner to square $(y,x)$.
We denote this sum $f(y,x)$, Let $f(y,x)$ denote this sum,
so $f(n,n)$ is the largest sum in a path so $f(n,n)$ is the largest sum on a path
from the upper-left corner to from the upper-left corner to
the lower-right corner. the lower-right corner.
The recursive formula is based on the observation The recursive formula is based on the observation
that a path that ends to square$(y,x)$ that a path that ends at square $(y,x)$
can either come from square $(y,x-1)$ can either come from square $(y,x-1)$
or from square $(y-1,x)$: or from square $(y-1,x)$:
\begin{center} \begin{center}
@ -647,12 +645,12 @@ D & 5 & 3 \\
\end{tabular} \end{tabular}
\end{center} \end{center}
\end{samepage} \end{samepage}
and the maximum total weight is 12, and the maximum allowed total weight is 12,
the optimal solution is to select objects $B$ and $D$. an optimal solution is to select objects $B$ and $D$.
Their total weight $6+5=11$ doesn't exceed 12, Their total weight $6+5=11$ does not exceed 12,
and their total value $3+3=6$ is as large as possible. and their total value $3+3=6$ is the largest possible.
This task is possible to solve in two different ways This task can be solved in two different ways
using dynamic programming. using dynamic programming.
We can either regard the problem as maximizing the We can either regard the problem as maximizing the
total value of the objects or total value of the objects or
@ -664,12 +662,12 @@ minimizing the total weight of the objects.
denote the largest possible total value denote the largest possible total value
when a subset of objects $1 \ldots k$ is selected when a subset of objects $1 \ldots k$ is selected
such that the total weight is $u$. such that the total weight is $u$.
The solution for the problem is The solution to the problem is
the largest value the largest value
$f(n,u)$ where $0 \le u \le x$. $f(n,u)$ where $0 \le u \le x$.
A recursive formula for calculating A recursive formula for calculating
the function is the function is
\[f(k,u) = \max(f(k-1,u),f(k-1,u-p_k)+a_k)\] \[f(k,u) = \max(f(k-1,u),f(k-1,u-p_k)+a_k),\]
because we can either include or not include because we can either include or not include
object $k$ in the solution. object $k$ in the solution.
The base cases are $f(0,0)=0$ and $f(0,u)=-\infty$ The base cases are $f(0,0)=0$ and $f(0,u)=-\infty$
@ -693,7 +691,7 @@ largest value $u$
for which $0 \le u \le s$ and $f(n,u) \le x$ for which $0 \le u \le s$ and $f(n,u) \le x$
where $s=\sum_{i=1}^n a_i$. where $s=\sum_{i=1}^n a_i$.
A recursive formula for calculating the function is A recursive formula for calculating the function is
\[f(k,u) = \min(f(k-1,u),f(k-1,u-a_k)+p_k).\] \[f(k,u) = \min(f(k-1,u),f(k-1,u-a_k)+p_k)\]
as in solution 1. as in solution 1.
The base cases are $f(0,0)=0$ and $f(0,u)=\infty$ The base cases are $f(0,0)=0$ and $f(0,u)=\infty$
when $u \neq 0$. when $u \neq 0$.
@ -704,8 +702,8 @@ that can be constructed using the following sequence:
\[f(4,6)=f(3,3)+5=f(2,3)+5=f(1,0)+6+5=f(0,0)+6+5=11.\] \[f(4,6)=f(3,3)+5=f(2,3)+5=f(1,0)+6+5=f(0,0)+6+5=11.\]
~\\ ~\\
It is interesting to note how the features of the input It is interesting to note how the parameters of the input
affect on the efficiency of the solutions. affect the efficiency of the solutions.
The efficiency of solution 1 depends on the weights The efficiency of solution 1 depends on the weights
of the objects, while the efficiency of solution 2 of the objects, while the efficiency of solution 2
depends on the values of the objects. depends on the values of the objects.
@ -715,10 +713,8 @@ depends on the values of the objects.
\index{edit distance} \index{edit distance}
\index{Levenshtein distance} \index{Levenshtein distance}
The \key{edit distance}, The \key{edit distance} or \key{Levenshtein distance}
also known as the \key{Levenshtein distance}, is the minimum number of editing operations
indicates how similar two strings are.
It is the minimum number of editing operations
needed for transforming the first string needed for transforming the first string
into the second string. into the second string.
The allowed editing operations are as follows: The allowed editing operations are as follows:
@ -735,13 +731,14 @@ because we can first perform operation
(change) and then operation (change) and then operation
\texttt{MOVE} $\rightarrow$ \texttt{MOVIE} \texttt{MOVE} $\rightarrow$ \texttt{MOVIE}
(insertion). (insertion).
This is the smallest possible number of operations This is the smallest possible number of operations,
because it is clear that one operation is not enough. because it is clear that it is not possible
to use only one operation.
Suppose we are given strings Suppose we are given strings
\texttt{x} of $n$ characters and \texttt{x} and \texttt{y} that contain
\texttt{y} of $m$ characters, $n$ and $m$ characters, respectively,
and we want to calculate the edit distance and we wish to calculate the edit distance
between them. between them.
This can be efficiently done using This can be efficiently done using
dynamic programming in $O(nm)$ time. dynamic programming in $O(nm)$ time.
@ -749,9 +746,7 @@ Let $f(a,b)$ denote the edit distance
between the first $a$ characters of \texttt{x} between the first $a$ characters of \texttt{x}
and the first $b$ characters of \texttt{y}. and the first $b$ characters of \texttt{y}.
Using this function, the edit distance between Using this function, the edit distance between
\texttt{x} and \texttt{y} is $f(n,m)$, \texttt{x} and \texttt{y} equals $f(n,m)$.
and the function also determines
the editing operations needed.
The base cases for the function are The base cases for the function are
\[ \[
@ -765,10 +760,10 @@ and in the general case the formula is
where $c=0$ if the $a$th character of \texttt{x} where $c=0$ if the $a$th character of \texttt{x}
equals the $b$th character of \texttt{y}, equals the $b$th character of \texttt{y},
and otherwise $c=1$. and otherwise $c=1$.
The formula covers all ways to shorten the strings: The formula considers all ways how to shorten the strings:
\begin{itemize} \begin{itemize}
\item $f(a,b-1)$ means that a character is inserted to \texttt{x} \item $f(a,b-1)$ means that a character is inserted to \texttt{x}
\item $f(a-1,b)$ means that a chacater is removed from \texttt{x} \item $f(a-1,b)$ means that a character is removed from \texttt{x}
\item $f(a-1,b-1)$ means that \texttt{x} and \texttt{y} contain \item $f(a-1,b-1)$ means that \texttt{x} and \texttt{y} contain
the same character ($c=0$), the same character ($c=0$),
or a character in \texttt{x} is transformed into or a character in \texttt{x} is transformed into
@ -898,11 +893,11 @@ the edit distance between \texttt{LOV} and \texttt{MOV}, etc.
\section{Counting tilings} \section{Counting tilings}
Sometimes the dynamic programming state Sometimes the states in a dynamic programming solution
is more complex than a fixed combination of numbers. are more complex than fixed combinations of numbers.
As an example, As an example,
we consider a problem where our task we consider the problem of calculating
is to calculate the number of different ways to the number of distinct ways to
fill an $n \times m$ grid using fill an $n \times m$ grid using
$1 \times 2$ and $2 \times 1$ size tiles. $1 \times 2$ and $2 \times 1$ size tiles.
For example, one valid solution For example, one valid solution
@ -949,16 +944,16 @@ $\sqsubset \sqsupset \sqsubset \sqsupset \sqsubset \sqsupset \sqcup$
\end{itemize} \end{itemize}
Let $f(k,x)$ denote the number of ways to Let $f(k,x)$ denote the number of ways to
construct a solution for the rows $1 \ldots k$ construct a solution for rows $1 \ldots k$
in the grid so that string $x$ corresponds to row $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 programing here,
because the state of a row is constrained because the state of a row is constrained
only be the state of the previous row. only by the state of the previous row.
A solution is valid if row $1$ doesn't contain A solution is valid if row $1$ does not contain
the character $\sqcup$, the character $\sqcup$,
row $n$ doesn't contain the character $\sqcap$, row $n$ does not contain the character $\sqcap$,
and all successive rows are \emph{compatible}. and all consecutive rows are \emph{compatible}.
For example, the rows For example, the rows
$\sqcup \sqsubset \sqsupset \sqcup \sqcap \sqcap \sqcup$ and $\sqcup \sqsubset \sqsupset \sqcup \sqcap \sqcap \sqcup$ and
$\sqsubset \sqsupset \sqsubset \sqsupset \sqcup \sqcup \sqcap$ $\sqsubset \sqsupset \sqsubset \sqsupset \sqcup \sqcup \sqcap$
@ -968,15 +963,15 @@ $\sqsubset \sqsupset \sqsubset \sqsupset \sqsubset \sqsupset \sqcup$
are not compatible. are not compatible.
Since a row consists of $m$ characters and there are Since a row consists of $m$ characters and there are
four choices for each character, the number of different four choices for each character, the number of distinct
rows is at most $4^m$. rows is at most $4^m$.
Thus, the time complexity of the solution is Thus, the time complexity of the solution is
$O(n 4^{2m})$ because we can check the $O(n 4^{2m})$ because we can go through the
$O(4^m)$ possible states for each row, $O(4^m)$ possible states for each row,
and for each state, there are $O(4^m)$ and for each state, there are $O(4^m)$
possible states for the previous row. possible states for the previous row.
In practice, it's a good idea to rotate the grid In practice, it is a good idea to rotate the grid
so that the shorter side has length $m$ so that the shorter side has length $m$,
because the factor $4^{2m}$ dominates the time complexity. because the factor $4^{2m}$ dominates the time complexity.
It is possible to make the solution more efficient It is possible to make the solution more efficient
@ -985,18 +980,18 @@ It turns out that it is sufficient to know the
columns of the previous row that contain the first square columns of the previous row that contain the first square
of a vertical tile. of a vertical tile.
Thus, we can represent a row using only characters Thus, we can represent a row using only characters
$\sqcap$ and $\Box$ where $\Box$ is a combination $\sqcap$ and $\Box$, where $\Box$ is a combination
of characters of characters
$\sqcup$, $\sqsubset$ and $\sqsupset$. $\sqcup$, $\sqsubset$ and $\sqsupset$.
In this case, there are only Using this representation, there are only
$2^m$ distinct rows and the time complexity becomes $2^m$ distinct rows and the time complexity is
$O(n 2^{2m})$. $O(n 2^{2m})$.
As a final note, there is also a surprising direct formula As a final note, there is also a surprising direct formula
for calculating the number of tilings: for calculating the number of tilings:
\[ \prod_{a=1}^{\lceil n/2 \rceil} \prod_{b=1}^{\lceil m/2 \rceil} 4 \cdot (\cos^2 \frac{\pi a}{n + 1} + \cos^2 \frac{\pi b}{m+1}).\] \[ \prod_{a=1}^{\lceil n/2 \rceil} \prod_{b=1}^{\lceil m/2 \rceil} 4 \cdot (\cos^2 \frac{\pi a}{n + 1} + \cos^2 \frac{\pi b}{m+1})\]
This formula is very efficient because it calculates This formula is very efficient, because it calculates
the number of tilings on $O(nm)$ time, the number of tilings in $O(nm)$ time,
but since the answer is a product of real numbers, but since the answer is a product of real numbers,
a practical problem in using the formula is a practical problem in using the formula is
how to store the intermediate results accurately. how to store the intermediate results accurately.