Revizia anterioară Revizia următoare
Solutii Autumn WarmUp 2019
Problema Sec
Nu exista inca...
Solutia Problemei Rogvaiv
Solutia este prezentata de unul din concurenti,
Solutia 1
Vom rezolva problema prin metoda programării dinamice.
Fie numărul de şiruri de lungime
care au pe ultima poziţie al
-lea caracter şi care conţin deja sau nu şirul
sau
în funcţie de cerinţă.
(considerăm primul </tex>V</tex> diferit de al doilea
)
(
,
)
Pentru cerinţa 1.
Observăm că putem să punem pe ultima poziţie orice caracter din primele 6 fără a forma .
Deci =
(
,
,
)
Pentru pot să formez şirul
.
.
reprezintă toate şirurile care conţin deja şirul
, iar
reprezintă numărul de subşiruri care au caracterul
pe prima poziţie şi nu conţin deja şirul dat. Deci pe poziţiile rămase voi completa cu caracterele necesare pentru a forma
.
=
(scad cazurile în care se formează
).
Pentru cerinţa 2.
Recurenţa este asemănătoare.
De data aceasta doar caracterele cu indicii nu pot forma
sau
.
Deci (
,
,
)
Cazul este identic cu cel de la cerinţa 1.
Pentru recurenţa devine:
Rezultatul şi complexitatea.
Rezultatul este
.
Complexitatea este .
Solutia 2.
Considerăm următoarele stări:
Starea 1 = (nu conţine niciun caracter)
Starea 2 =
Starea 3 =
.
.
Starea 7 =
Astfel putem să ne folosim de dinamica = numărul de şiruri de lungime
care se află în starea
şi care conţin deja sau nu unul din şirurile date.
Pentru cerinţa 1.
La adăugarea unui caracter nou observăm că ori transformă starea în starea
ori îl aduce la starea 1. Când se formeaza
devine 1 şi starea devine 0 (
).
Pentru cerinta 2.
Adăugăm încă 7 stări care corespund şirului inversat (), iar recurenţa este la fel.
Starea 8:
Starea 9:
Starea 10:
.
.
Starea 14:
Răspunsul şi complexitatea.
Răspunsul este
.
Complexitatea este
O optimizare care nu era necesară pentru a obţine punctaj maxim.
Menţionez că soluţia a doua poate fi îmbunătăţită.
Dacă liniarizăm dinamica putem să formăm o matrice care ridicată la puterea conţine valorile din care este format rezultatul.
Astfel complexitatea devine .
Soluţia problemei Shoturi
Solutia este prezentata de unul din concurenti,
Soluţie backtracking - 10 puncte
Generăm toate variantele posibile ale vectorului pentru care
. Această soluţie obţine 0 sau 10 puncte în funcţie de implementarea backtrackingului, care ar avea complexitatea
amortizat
Soluţie N*K2 - 50 de puncte
Această soluţie presupune tehnica programării dinamice. Vom folosi matricea dp[n][k], pentru care:
- dp[i][j] = suma potenţelor tuturor amestecurilor posibile ingerând j
shoturipăhărele din primele isubstanţe interzisesucuri
De aici deducem recurenţa:
De ce? Pentru că, din cum am definit dinamica, dp[i-1][x] = suma potenţelor tuturor amestecurilor posibile ingerând x păhărele din primele i-1 sucuri. Cum dp[i][j] presupune ingerarea a j pahare => se vor ingera j-x pahare de tipul i, care vor inmulţi fiecare potenţă cu . Adunăm dp[i-1][j] la dp[i][j] deoarece tinerii pot alege să nu bea niciun shot din cazanul i.
Observatie: ,
, ca să ne iasă calculele :)
Voi exemplifica explicaţia cu cod în limbajul C++
dp[1][0]=1;
for(j=1; j<=k; j++)
dp[1][j]=(1LL*j*hazard[1])%MOD;
for(i=2; i<=n; i++)
{
dp[i][0]=1;
for(j=1; j<=k; j++)
{
for(x=0; x<=j-1; x++)
dp[i][j]=(dp[i][j]+1LL*dp[i-1][x]*(j-x)*hazard[i])%MOD;
dp[i][j]=(dp[i][j]+dp[i-1][j])%MOD;
}
}
Obs: MOD=269 696 969 Nice
Solutie N*K, memorie N*K - 80 de puncte
Trecerea de la O(N*K2) la O(N*K) se face cu ajutorul sumelor parţiale.
Observăm că, dacă atunci când parcurgem cu j-ul ţinem o variabilă suma = pe care o updatăm adăugând
şi o variabila suma_de_suma pe care o updatăm cu suma, acestea vor arata aşa:
j | suma | suma_de_suma |
---|---|---|
1 | 1*dp[i-1][0] 1*dp[i-1][j-1] | 1*dp[i-1][0] 1*dp[i-1][j-1] |
2 | 1*dp[i-1][0]+1*dp[i-1][1] 1*dp[i-1][j-2]+1*dp[i-1][j-1] | 2*dp[i-1][0]+1*dp[i-1][1] 2*dp[i-1][j-2]+1*dp[i-1][j-1] |
3 | 1*dp[i-1][0]+1*dp[i-1][1]+1*dp[i-1][2] 1*dp[i-1][j-3]+1*dp[i-1][j-2]+1*dp[i-1][j-1] | 3*dp[i-1][0]+2*dp[i-1][1]+1*dp[i-1][2] 3*dp[i-1][j-3]+2*dp[i-1][j-2]+1*dp[i-1][j-1] |
Se observă că, inmulţind suma_de_suma cu , obţinem rezlultatul pentru dp[i][j].
Cum suma şi suma_de_suma sunt calculate in timpul parcurgerii cu j, complexitatea este
dp[1][0]=1;
for(i=1; i<=k; i++)
dp[1][i]=(1LL*i*hazard[1])%MOD;
for(i=2; i<=n; i++)
{
dp[i][0]=1;
suma=1;
suma_de_suma=1;
for(j=1; j<=k; j++)
{
dp[i][j]+=(1LL*suma_de_suma*hazard[i]+dp[i-1][j])%MOD;
suma=(suma+dp[i-1][j])%MOD;
suma_de_suma=(suma_de_suma+suma)%MOD;
}
}
Soluţie N*K, memorie N - 100 de puncte
Deoarece pentru a calcula avem nevoie doar de
, folosim doar 2 linii din matrice, de aceea putem să scăpăm de ea şi să păstrăm doar 2 vectori reprezentând ultimele 2 linii in ordinea parcurgerii. Astfel,
va fi fostul
, iar
va fi fostul
. De aceea, memoria folosită este
în loc de
.
dp[0]=1;
for(i=1; i<=k; i++)
dp[i]=(1LL*i*hazard[1])%MOD;
for(i=2; i<=n; i++)
{
for(j=0; j<=k; j++)
aux[j]=dp[j];
dp[0]=1;
suma=1;
suma_de_suma=1;
for(j=1; j<=k; j++)
{
dp[j]=(1LL*suma_de_suma*hazard[i]+aux[j])%MOD;
suma=suma+aux[j];
if(suma>=MOD)
suma-=MOD;
suma_de_suma=suma_de_suma+suma;
if(suma_de_suma>=MOD)
suma_de_suma-=MOD;
}
}
NOTA: Deoarece suma, suma_de_suma şi aux sunt deja , adunarea lor va fi
de aceea este necesar doar să scădem
din ele când sunt
pentru a fi, din nou,