Coin problem
This commit is contained in:
parent
12d094ac8a
commit
9b75316ad6
440
luku07.tex
440
luku07.tex
|
@ -1,87 +1,86 @@
|
||||||
\chapter{Dynamic programming}
|
\chapter{Dynamic programming}
|
||||||
|
|
||||||
\index{dynaaminen ohjelmointi@dynaaminen ohjelmointi}
|
\index{dynamic programming}
|
||||||
|
|
||||||
\key{Dynaaminen ohjelmointi}
|
\key{Dynamic programming}
|
||||||
on tekniikka, joka yhdistää täydellisen haun
|
is a technique that combines the correctness
|
||||||
toimivuuden ja ahneiden algoritmien tehokkuuden.
|
of complete search and the efficiency
|
||||||
Dynaamisen ohjelmoinnin käyttäminen edellyttää,
|
of greedy algorithms.
|
||||||
että tehtävä jakautuu osaongelmiin,
|
Dynamic programming can be used if the
|
||||||
jotka voidaan käsitellä toisistaan riippumattomasti.
|
problem can be divided into subproblems
|
||||||
|
that can be calculated independently.
|
||||||
|
|
||||||
Dynaamisella ohjelmoinnilla on kaksi käyttötarkoitusta:
|
There are two uses for dynamic programming:
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item
|
\item
|
||||||
\key{Optimiratkaisun etsiminen}:
|
\key{Findind an optimal solution}:
|
||||||
Haluamme etsiä ratkaisun, joka on
|
We want to find a solution that is
|
||||||
jollakin tavalla suurin mahdollinen
|
as large as possible or as small as possible.
|
||||||
tai pienin mahdollinen.
|
|
||||||
\item
|
\item
|
||||||
\key{Ratkaisuiden määrän laskeminen}:
|
\key{Couting the number of solutions}:
|
||||||
Haluamme laskea, kuinka monta mahdollista
|
We want to calculate the total number of
|
||||||
ratkaisua on olemassa.
|
possible solutions.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
Tutustumme dynaamiseen ohjelmointiin ensin
|
We will first see how dynamic programming can
|
||||||
optimiratkaisun etsimisen kautta ja käytämme sitten
|
be used for finding an optimal solution,
|
||||||
samaa ideaa ratkaisujen määrän laskemiseen.
|
and then we will use the same idea for
|
||||||
|
counting the solutions.
|
||||||
|
|
||||||
Dynaamisen ohjelmoinnin ymmärtäminen on yksi merkkipaalu
|
Understanding dynamic programming is a milestone
|
||||||
jokaisen kisakoodarin uralla.
|
in every competitive programmer's career.
|
||||||
Vaikka menetelmän perusidea on yksinkertainen,
|
While the basic idea of the technique is simple,
|
||||||
haasteena on oppia soveltamaan sitä sujuvasti
|
the challenge is how to apply it for different problems.
|
||||||
erilaisissa tehtävissä.
|
This chapter introduces a set of classic problems
|
||||||
Tämä luku esittelee joukon
|
that are a good starting point.
|
||||||
perusesimerkkejä, joista on hyvä lähteä liikkeelle.
|
|
||||||
|
|
||||||
\section{Kolikkotehtävä}
|
\section{Coin problem}
|
||||||
|
|
||||||
Aloitamme dynaamisen ohjelmoinnin tutun tehtävän kautta:
|
We first consider a problem that we
|
||||||
Muodostettavana on rahamäärä $x$
|
have already seen:
|
||||||
käyttäen mahdollisimman vähän kolikoita.
|
Given a set of coin values $\{c_1,c_2,\ldots,c_k\}$
|
||||||
Kolikoiden arvot ovat $\{c_1,c_2,\ldots,c_k\}$
|
and a sum of money $x$, our task is to
|
||||||
ja jokaista kolikkoa on saatavilla rajattomasti.
|
form the sum $x$ using as few coins as possible.
|
||||||
|
|
||||||
Luvussa 6.1 ratkaisimme tehtävän ahneella algoritmilla,
|
In Chapter 6.1, we solved the problem using a
|
||||||
joka muodostaa rahamäärän valiten mahdollisimman
|
greedy algorithm that always selects the largest
|
||||||
suuria kolikoita.
|
possible coin for the sum.
|
||||||
Ahne algoritmi toimii esimerkiksi silloin,
|
The greedy algorithm works, for example,
|
||||||
kun kolikot ovat eurokolikot,
|
when the coins are the euro coins,
|
||||||
mutta yleisessä tapauksessa ahne algoritmi
|
but in the general case the greedy algorithm
|
||||||
ei välttämättä valitse pienintä määrää kolikoita.
|
doesn't necessarily produce an optimal solution.
|
||||||
|
|
||||||
Nyt on aika ratkaista tehtävä tehokkaasti
|
Now it's time to solve the problem efficiently
|
||||||
dynaamisella ohjelmoinnilla niin,
|
using dynamic programming, so that the algorithms
|
||||||
että algoritmi toimii millä tahansa kolikoilla.
|
works for any coin set.
|
||||||
Algoritmi perustuu rekursiiviseen funktioon,
|
The dynamic programming
|
||||||
joka käy läpi kaikki vaihtoehdot rahamäärän
|
algorithm is based on a recursive function
|
||||||
muodostamiseen täydellisen haun kaltaisesti.
|
that goes through all possibilities how to
|
||||||
Algoritmi toimii kuitenkin tehokkaasti, koska
|
select the coins, like a brute force algorithm.
|
||||||
se tallentaa välituloksia muistitaulukkoon,
|
However, the dynamic programming
|
||||||
minkä ansiosta sen ei tarvitse laskea samoja
|
algorithm is efficient because
|
||||||
asioita moneen kertaan.
|
it uses memoization to
|
||||||
|
calculate the answer for each subproblem only once.
|
||||||
|
|
||||||
\subsubsection{Rekursiivinen esitys}
|
\subsubsection{Recursive formulation}
|
||||||
|
|
||||||
\index{rekursioyhtxlz@rekursioyhtälö}
|
The idea in dynamic programming is to
|
||||||
|
formulate the problem recursively so
|
||||||
|
that the answer for the problem can be
|
||||||
|
calculated from the answers for the smaller
|
||||||
|
subproblems.
|
||||||
|
In this case, a natural problem is as follows:
|
||||||
|
what is the smallest number of coins
|
||||||
|
required for constructing sum $x$?
|
||||||
|
|
||||||
Dynaamisessa ohjelmoinnissa on ideana esittää
|
Let $f(x)$ be a function that gives the answer
|
||||||
ongelma rekursiivisesti niin,
|
for the problem, i.e., $f(x)$ is the smallest
|
||||||
että ongelman ratkaisun voi laskea
|
number of coins required for constructing sum $x$.
|
||||||
saman ongelman pienempien tapausten ratkaisuista.
|
The values of the function depend on the
|
||||||
Tässä tehtävässä luonteva ongelma on seuraava:
|
values of the coins.
|
||||||
mikä on pienin määrä kolikoita,
|
For example, if the values are $\{1,3,4\}$,
|
||||||
joilla voi muodostaa rahamäärän $x$?
|
the first values of the function are as follows:
|
||||||
|
|
||||||
Merkitään $f(x)$ funktiota,
|
|
||||||
joka antaa vastauksen ongelmaan,
|
|
||||||
eli $f(x)$ on pienin määrä kolikoita,
|
|
||||||
joilla voi muodostaa rahamäärän $x$.
|
|
||||||
Funktion arvot riippuvat siitä,
|
|
||||||
mitkä kolikot ovat käytössä.
|
|
||||||
Esimerkiksi jos kolikot ovat $\{1,3,4\}$,
|
|
||||||
funktion ensimmäiset arvot ovat:
|
|
||||||
|
|
||||||
\[
|
\[
|
||||||
\begin{array}{lcl}
|
\begin{array}{lcl}
|
||||||
|
@ -99,44 +98,46 @@ f(10) & = & 3 \\
|
||||||
\end{array}
|
\end{array}
|
||||||
\]
|
\]
|
||||||
|
|
||||||
Nyt $f(0)=0$, koska jos rahamäärä on 0,
|
First, $f(0)=0$ because no coins are needed
|
||||||
ei tarvita yhtään kolikkoa.
|
for sum $0$.
|
||||||
Vastaavasti $f(3)=1$, koska rahamäärän 3
|
Moreover, $f(3)=1$ because the sum $3$
|
||||||
voi muodostaa kolikolla 3,
|
can be formed using coin 3,
|
||||||
ja $f(5)=2$, koska rahamäärän 5
|
and $f(5)=2$ because the sum 5 can
|
||||||
voi muodostaa kolikoilla 1 ja 4.
|
be formed using coins 1 and 4.
|
||||||
|
|
||||||
Oleellinen ominaisuus funktiossa on,
|
The essential property in the function is
|
||||||
että arvon $f(x)$ pystyy laskemaan
|
that the value $f(x)$ can be calculated
|
||||||
rekursiivisesti käyttäen pienempiä
|
recursively from the smaller values of the function.
|
||||||
funktion arvoja.
|
For example, if the coin set is $\{1,3,4\}$,
|
||||||
Esimerkiksi jos kolikot ovat $\{1,3,4\}$,
|
there are three ways to select the first coin
|
||||||
on kolme tapaa alkaa muodostaa rahamäärää $x$:
|
in a solution: we can choose coin 1, 3 or 4.
|
||||||
valitaan kolikko 1, 3 tai 4.
|
If coin 1 is chosen, the remaining task is to
|
||||||
Jos valitaan kolikko 1, täytyy
|
form the sum $x-1$.
|
||||||
muodostaa vielä rahamäärä $x-1$.
|
Similarly, if coin 3 or 4 is chosen,
|
||||||
Vastaavasti jos valitaan kolikko 3 tai 4,
|
we should form the sum $x-3$ or $x-4$.
|
||||||
täytyy muodostaa rahamäärä $x-3$ tai $x-4$.
|
|
||||||
|
|
||||||
Niinpä rekursiivinen kaava on
|
Thus, the recursive formula is
|
||||||
\[f(x) = \min(f(x-1),f(x-3),f(x-4))+1,\]
|
\[f(x) = \min(f(x-1),f(x-3),f(x-4))+1\]
|
||||||
missä funktio $\min$ valitsee pienimmän parametreistaan.
|
where the function $\min$ returns the smallest
|
||||||
Yleisemmin jos kolikot ovat $\{c_1,c_2,\ldots,c_k\}$,
|
of its parameters.
|
||||||
rekursiivinen kaava on
|
In the general case, for the coin set
|
||||||
|
$\{c_1,c_2,\ldots,c_k\}$,
|
||||||
|
the recursive formula is
|
||||||
\[f(x) = \min(f(x-c_1),f(x-c_2),\ldots,f(x-c_k))+1.\]
|
\[f(x) = \min(f(x-c_1),f(x-c_2),\ldots,f(x-c_k))+1.\]
|
||||||
Funktion pohjatapauksena on
|
The base case for the function is
|
||||||
\[f(0)=0,\]
|
\[f(0)=0,\]
|
||||||
koska rahamäärän 0 muodostamiseen ei tarvita
|
because no coins are needed for constructing
|
||||||
yhtään kolikkoa.
|
the sum 0.
|
||||||
Lisäksi on hyvä määritellä
|
In addition, it's a good idea to define
|
||||||
\[f(x)=\infty,\hspace{8px}\textrm{jos $x<0$}.\]
|
\[f(x)=\infty,\hspace{8px}\textrm{jos $x<0$}.\]
|
||||||
Tämä tarkoittaa, että negatiivisen rahamäärän
|
This means that an infinite number of coins
|
||||||
muodostaminen vaatii äärettömästi kolikoita,
|
is needed to create a negative sum of money.
|
||||||
mikä estää sen, että rekursio muodostaisi
|
This prevents the situation that the recursive
|
||||||
ratkaisun, johon kuuluu negatiivinen rahamäärä.
|
function would form a solution where the
|
||||||
|
initial sum of money is negative.
|
||||||
|
|
||||||
Nyt voimme toteuttaa funktion C++:lla suoraan
|
Now it's possible to implement the function in C++
|
||||||
rekursiivisen määritelmän perusteella:
|
directly using the recursive definition:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int f(int x) {
|
int f(int x) {
|
||||||
|
@ -150,41 +151,42 @@ int f(int x) {
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Koodi olettaa, että käytettävät kolikot ovat
|
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]$,
|
||||||
ja arvo $10^9$ kuvastaa ääretöntä.
|
and the value $10^9$ means infinity.
|
||||||
Tämä on toimiva funktio, mutta se ei ole vielä tehokas,
|
This function works but it is not efficient yet
|
||||||
koska funktio käy läpi valtavasti erilaisia tapoja
|
because it goes through a large number
|
||||||
muodostaa rahamäärä.
|
of ways to construct the sum.
|
||||||
Seuraavaksi esiteltävä muistitaulukko tekee
|
However, the function becomes efficient by
|
||||||
funktiosta tehokkaan.
|
using memoization.
|
||||||
|
|
||||||
\subsubsection{Muistitaulukko}
|
\subsubsection{Memoization}
|
||||||
|
|
||||||
\index{muistitaulukko@muistitaulukko}
|
\index{memoization}
|
||||||
|
|
||||||
Dynaaminen ohjelmointi tehostaa
|
Dynamic programming allows to calculate the
|
||||||
rekursiivisen funktion laskentaa
|
value of a recursive function efficiently
|
||||||
tallentamalla funktion arvoja \key{muistitaulukkoon}.
|
using \key{memoization}.
|
||||||
Taulukon avulla funktion arvo
|
This means that an auxiliary array is used
|
||||||
tietyllä parametrilla riittää laskea
|
for storing the values of the function
|
||||||
vain kerran, minkä jälkeen sen voi
|
for different parameters.
|
||||||
hakea suoraan taulukosta.
|
For each parameter, the value of the function
|
||||||
Tämä muutos nopeuttaa algoritmia ratkaisevasti.
|
is calculated only once, and after this,
|
||||||
|
it can be directly retrieved from the array.
|
||||||
Tässä tehtävässä muistitaulukoksi sopii taulukko
|
|
||||||
|
|
||||||
|
In this problem, we can use the array
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int d[N];
|
int d[N];
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
jonka kohtaan $\texttt{d}[x]$
|
where $\texttt{d}[x]$ will contain
|
||||||
lasketaan funktion arvo $f(x)$.
|
the value $f(x)$.
|
||||||
Vakio $N$ valitaan niin, että kaikki
|
The constant $N$ should be chosen so
|
||||||
laskettavat funktion arvot mahtuvat taulukkoon.
|
that there is space for all needed
|
||||||
|
values of the function.
|
||||||
|
|
||||||
Tämän jälkeen funktion voi toteuttaa
|
After this, the function can be efficiently
|
||||||
tehokkaasti näin:
|
implemented as follows:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int f(int x) {
|
int f(int x) {
|
||||||
|
@ -200,32 +202,34 @@ int f(int x) {
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Funktio käsittelee pohjatapaukset $x=0$
|
The function handles the base cases
|
||||||
ja $x<0$ kuten ennenkin.
|
$x=0$ and $x<0$ as previously.
|
||||||
Sitten funktio tarkastaa,
|
Then the function checks if
|
||||||
onko $f(x)$ laskettu jo taulukkoon $\texttt{d}[x]$.
|
$f(x)$ has already been calculated
|
||||||
Jos $f(x)$ on laskettu,
|
and stored to $\texttt{d}[x]$.
|
||||||
funktio palauttaa sen suoraan.
|
If $f(x)$ can be found in the array,
|
||||||
Muussa tapauksessa funktio laskee arvon rekursiivisesti
|
the function directly returns it.
|
||||||
ja tallentaa sen kohtaan $\texttt{d}[x]$.
|
Otherwise the function calculates the value
|
||||||
|
recursively and stores it to $\texttt{d}[x]$.
|
||||||
|
|
||||||
Muistitaulukon ansiosta funktio toimii
|
Using memoization the function works
|
||||||
nopeasti, koska sen tarvitsee laskea
|
efficiently because it is needed to
|
||||||
vastaus kullekin $x$:n arvolle
|
recursively calculate
|
||||||
vain kerran rekursiivisesti.
|
the answer for each $x$ only once.
|
||||||
Heti kun arvo $f(x)$ on tallennettu muistitaulukkoon,
|
After a value $f(x)$ has been stored to the array,
|
||||||
sen saa haettua sieltä suoraan,
|
it can be directly retrieved whenever the
|
||||||
kun funktiota kutsutaan seuraavan kerran parametrilla $x$.
|
function will be called again with parameter $x$.
|
||||||
|
|
||||||
Tuloksena olevan algoritmin aikavaativuus on $O(xk)$,
|
The time complexity of the resulting algorithm
|
||||||
kun rahamäärä on $x$ ja kolikoiden määrä on $k$.
|
is $O(xk)$ when the sum is $x$ and the number of
|
||||||
Käytännössä ratkaisu on mahdollista toteuttaa,
|
coins is $k$.
|
||||||
jos $x$ on niin pieni, että on mahdollista varata
|
In practice, the algorithm is usable if
|
||||||
riittävän suuri muistitaulukko.
|
$x$ is so small that it is possible to allocate
|
||||||
|
an array for all possible function parameters.
|
||||||
|
|
||||||
Huomaa, että muistitaulukon voi muodostaa
|
Note that the array can also be constructed using
|
||||||
myös suoraan silmukalla ilman rekursiota
|
a loop that calculates all the values
|
||||||
laskemalla arvot pienimmästä suurimpaan:
|
instead of a recursive function:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
d[0] = 0;
|
d[0] = 0;
|
||||||
for (int i = 1; i <= x; i++) {
|
for (int i = 1; i <= x; i++) {
|
||||||
|
@ -238,31 +242,29 @@ for (int i = 1; i <= x; i++) {
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Silmukkatoteutus on lyhyempi ja
|
This implementation is shorter and somewhat
|
||||||
hieman tehokkaampi kuin rekursiototeutus,
|
more efficient than recursion,
|
||||||
minkä vuoksi kokeneet kisakoodarit
|
and experienced competitive programmers
|
||||||
toteuttavat dynaamisen ohjelmoinnin
|
often implement dynamic programming solutions
|
||||||
usein silmukan avulla.
|
using loops.
|
||||||
Kuitenkin silmukkatoteutuksen taustalla
|
Still, the underlying idea is the same as
|
||||||
on sama rekursiivinen idea kuin ennenkin.
|
in the recursive function.
|
||||||
|
|
||||||
\subsubsection{Ratkaisun muodostaminen}
|
\subsubsection{Constructing the solution}
|
||||||
|
|
||||||
Joskus optimiratkaisun arvon selvittämisen lisäksi
|
Sometimes it is not enough to find out the value
|
||||||
täytyy muodostaa näytteeksi yksi mahdollinen optimiratkaisu.
|
of the optimal solution, but we should also give
|
||||||
Tässä tehtävässä tämä tarkoittaa,
|
an example how such a solution can be constructed.
|
||||||
että ohjelman täytyy antaa esimerkki
|
In this problem, this means that the algorithm
|
||||||
tavasta valita kolikot,
|
should show how to select the coins that produce
|
||||||
joista muodostuu rahamäärä $x$
|
the sum $x$ using as few coins as possible.
|
||||||
käyttäen mahdollisimman vähän kolikoita.
|
|
||||||
|
|
||||||
Ratkaisun muodostaminen onnistuu lisäämällä
|
We can construct the solution by adding another
|
||||||
koodiin uuden taulukon, joka kertoo
|
array to the code. The array indicates for
|
||||||
kullekin rahamäärälle,
|
each sum of money the first coin that should be
|
||||||
mikä kolikko siitä tulee poistaa
|
chosen in an optimal solution.
|
||||||
optimiratkaisussa.
|
In the following code, the array \texttt{e}
|
||||||
Seuraavassa koodissa taulukko \texttt{e}
|
is used for this:
|
||||||
huolehtii asiasta:
|
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
d[0] = 0;
|
d[0] = 0;
|
||||||
|
@ -279,8 +281,8 @@ for (int i = 1; i <= x; i++) {
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Tämän jälkeen rahamäärän $x$ muodostavat
|
After this, we can print the coins needed
|
||||||
kolikot voi tulostaa näin:
|
for the sum $x$ as follows:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
while (x > 0) {
|
while (x > 0) {
|
||||||
|
@ -289,14 +291,15 @@ while (x > 0) {
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
\subsubsection{Ratkaisuiden määrän laskeminen}
|
\subsubsection{Counting the number of solutions}
|
||||||
|
|
||||||
Tarkastellaan sitten kolikkotehtävän muunnelmaa,
|
Let us now consider a variation of the problem
|
||||||
joka on muuten samanlainen kuin ennenkin,
|
that it's like the original problem but we should
|
||||||
mutta laskettavana on mahdollisten ratkaisuiden yhteismäärä
|
count the total number of solutions instead
|
||||||
optimaalisen ratkaisun sijasta.
|
of finding the optimal solution.
|
||||||
Esimerkiksi jos kolikot ovat $\{1,3,4\}$ ja rahamäärä on 5,
|
For example, if the coins are $\{1,3,4\}$ and
|
||||||
niin ratkaisuja on kaikkiaan 6:
|
the required sum is $5$,
|
||||||
|
there are a total of 6 solutions:
|
||||||
|
|
||||||
\begin{multicols}{2}
|
\begin{multicols}{2}
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
|
@ -309,29 +312,30 @@ niin ratkaisuja on kaikkiaan 6:
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
\end{multicols}
|
\end{multicols}
|
||||||
|
|
||||||
Ratkaisujen määrän laskeminen tapahtuu melko samalla tavalla
|
The number of the solutions can be calculated
|
||||||
kuin optimiratkaisun etsiminen.
|
using the same idea as finding the optimal solution.
|
||||||
Erona on, että optimiratkaisun etsivässä rekursiossa
|
The difference is that when finding the optimal solution,
|
||||||
valitaan pienin tai suurin aiempi arvo,
|
we maximize or minimize something in the recursion,
|
||||||
kun taas ratkaisujen määrän laskevassa rekursiossa lasketaan
|
but now we will sum together all possible alternatives to
|
||||||
yhteen kaikki vaihtoehdot.
|
construct a solution.
|
||||||
|
|
||||||
Tässä tapauksessa voimme määritellä funktion $f(x)$,
|
In this case, we can define a function $f(x)$
|
||||||
joka kertoo, monellako tavalla rahamäärän $x$
|
that returns the number of ways to construct
|
||||||
voi muodostaa kolikoista.
|
the sum $x$ using the coins.
|
||||||
Esimerkiksi $f(5)=6$, kun kolikot ovat $\{1,3,4\}$.
|
For example, $f(5)=6$ when the coins are $\{1,3,4\}$.
|
||||||
Funktion $f(x)$ saa laskettua rekursiivisesti kaavalla
|
The function $f(x)$ can be recursively calculated
|
||||||
\[ f(x) = f(x-c_1)+f(x-c_2)+\cdots+f(x-c_k),\]
|
using the formula
|
||||||
koska rahamäärän $x$ muodostamiseksi pitää
|
\[ f(x) = f(x-c_1)+f(x-c_2)+\cdots+f(x-c_k)\]
|
||||||
valita jokin kolikko $c_i$ ja muodostaa sen jälkeen rahamäärä $x-c_i$.
|
because to form the sum $x$ we should first
|
||||||
Pohjatapauksina ovat $f(0)=1$, koska rahamäärä 0 syntyy
|
choose some coin $c_i$ and after this form the sum $x-c_i$.
|
||||||
ilman yhtään kolikkoa,
|
The base cases are $f(0)=1$ because there is exactly
|
||||||
sekä $f(x)=0$, kun $x<0$, koska negatiivista rahamäärää
|
one way to form the sum 0 using an empty set of coins,
|
||||||
ei ole mahdollista muodostaa.
|
and $f(x)=0$, when $x<0$, because it's not possible
|
||||||
|
to form a negative sum of money.
|
||||||
|
|
||||||
Yllä olevassa esimerkissä funktioksi tulee
|
In the above example the function becomes
|
||||||
\[ f(x) = f(x-1)+f(x-3)+f(x-4) \]
|
\[ f(x) = f(x-1)+f(x-3)+f(x-4) \]
|
||||||
ja funktion ensimmäiset arvot ovat:
|
and the first values of the function are:
|
||||||
\[
|
\[
|
||||||
\begin{array}{lcl}
|
\begin{array}{lcl}
|
||||||
f(0) & = & 1 \\
|
f(0) & = & 1 \\
|
||||||
|
@ -347,9 +351,9 @@ f(9) & = & 40 \\
|
||||||
\end{array}
|
\end{array}
|
||||||
\]
|
\]
|
||||||
|
|
||||||
Seuraava koodi laskee funktion $f(x)$ arvon
|
The following code calculates the value $f(x)$
|
||||||
dynaamisella ohjelmoinnilla täyttämällä taulukon
|
using dynamic programming by filling the array
|
||||||
\texttt{d} rahamäärille $0 \ldots x$:
|
\texttt{d} for parameters $0 \ldots x$:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
d[0] = 1;
|
d[0] = 1;
|
||||||
|
@ -361,27 +365,29 @@ for (int i = 1; i <= x; i++) {
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Usein ratkaisujen määrä on niin suuri, että sitä ei tarvitse
|
Often the number of the solutions is so large
|
||||||
laskea kokonaan vaan riittää ilmoittaa vastaus
|
that it is not required to calculate the exact number
|
||||||
modulo $m$, missä esimerkiksi $m=10^9+7$.
|
but it is enough to give the answer modulo $m$
|
||||||
Tämä onnistuu muokkaamalla koodia niin,
|
where, for example, $m=10^9+7$.
|
||||||
että kaikki laskutoimitukset lasketaan modulo $m$.
|
This can be done by changing the code so that
|
||||||
Tässä tapauksessa riittää lisätä rivin
|
all calculations will be done in modulo $m$.
|
||||||
\begin{lstlisting}
|
In this case, it is enough to add the line
|
||||||
d[i] += d[i-c[j]];
|
|
||||||
\end{lstlisting}
|
|
||||||
jälkeen rivi
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
d[i] %= m;
|
d[i] %= m;
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
after the line
|
||||||
|
\begin{lstlisting}
|
||||||
|
d[i] += d[i-c[j]];
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
Nyt olemme käyneet läpi kaikki dynaamisen
|
Now we have covered all basic
|
||||||
ohjelmoinnin perusasiat.
|
techniques related to
|
||||||
Dynaamista ohjelmointia voi soveltaa monilla
|
dynamic programming.
|
||||||
tavoilla erilaisissa tilanteissa,
|
Since dynamic programming can be used
|
||||||
minkä vuoksi tutustumme seuraavaksi
|
in many different situations,
|
||||||
joukkoon tehtäviä, jotka esittelevät
|
we will now go through a set of problems
|
||||||
dynaamisen ohjelmoinnin mahdollisuuksia.
|
that show further examples how dynamic
|
||||||
|
programming can be used.
|
||||||
|
|
||||||
\section{Pisin nouseva alijono}
|
\section{Pisin nouseva alijono}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue