Code Submission Evaluation System Login

Algoritmit ongelmanratkaisussa 2019


Tasks | Statistics


CSES - Dynaaminen ohjelmointi 2/2

Dynaaminen ohjelmointi 2/2


Tällä viikolla jatkamme dynaamisen ohjelmoinnin harjoittelua. Tällä viikolla erityisesti Tirakirjan luvun 9 esimerkki repunpakkauksesta on hyödyllinen.

Seuraavaksi käymme läpi yhden lisäesimerkin dynaamisesta ohjelmoinnista.

Tehtävä

Annettuna on joukko kolikoita, joiden arvot ovat $\{c_1,c_2,\dots,c_k\}$. Tehtävänä on laskea, monellako tavalla kolikoista voi muodostaa rahamäärän $n$, kun jokaista kolikkoa saa käyttää miten monta kertaa tahansa.

Esimerkiksi jos kolikot ovat $\{2,3,5\}$ ja $n=7$, vastaus on $5$, koska vaihtoehdot ovat $2+2+3$, $2+3+2$, $3+2+2$, $2+5$ ja $5+2$.

Ratkaisu

Voimme ratkaista tehtävän kahdella for-silmukalla, joista ulompi käy läpi kaikki rahamäärät $1 \dots n$ ja sisempi käy läpi kolikot. Laskemme jokaiselle rahamäärälle, monellako tavalla sen voi muodostaa kolikoista.

Seuraavassa koodissa $d[x]$ ilmaisee, monellako tavalla rahamäärän $x$ voi muodostaa kolikoista. Alussa asetetaan $d[0]=1$, koska tyhjän rahamäärän voi muodostaa yhdellä tavalla (ei kolikoita).
d[0] = 1;
for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= k; j++) {
        if (i-c[j] >= 0) d[i] += d[i-c[j]];
    }
}
cout << d[n] << "\n";

Muunnelma

Luonteva muunnelma tehtävälle on, että haluamme laskea vain aidosti erilaiset tavat. Esimerkiksi tavat $2+2+3$ ja $2+3+2$ ovat oikeastaan samat, koska kolikot ovat samat mutta ne ovat vain eri järjestyksessä.

Tässä muunnelmassa esimerkkitapauksen vastaus on $2$, koska erilaiset tavat ovat $2+2+3$ ja $2+5$.

Ratkaisu

Yllättävää kyllä, saamme ratkaistua muunnelman muuttamalla vain vähän aiempaa koodia: riittää vaihtaa keskenään silmukoiden rungot.
d[0] = 1;
for (int j = 1; j <= k; j++) {
    for (int i = 1; i <= n; i++) {
        if (i-c[j] >= 0) d[i] += d[i-c[j]];
    }
}
cout << d[n] << "\n";

Nyt koodi käy läpi kolikot yksi kerrallaan ja jokaisen kohdalla kaikki rahamäärät läpi. Jokaisen kolikon käsittelyn jälkeen taulukko $d$ kertoo jokaisesta rahamäärästä, monellako erilaisella tavalla kyseisen rahamäärän voi muodostaa käyttämällä siihen asti käsiteltyjä kolikkoja.