Gordon Ramsay

O primă observaţie trivială este aceea că xi ≤ fri unde fri este numărul de apariţii al alimentului i.

Soluţie cu backtracking: 10 puncte

Generăm toate variantele posibile ale vectorului x pentru toate t-urile posibile şi luăm soluţia care aduce costul maxim.

Soluţie în O(N3): 25 puncte

A doua observaţie trivială este că alimentele sunt independente. Dacă ne fixăm un t, atunci putem calcula pentru fiecare aliment în parte x-ul care aduce profit cât mai mare pentru alimentul respectiv.
Astfel soluţia este să iterăm prin toate t-urile, iar pentru fiecare aliment i încercăm toate xi de la 0 până la fri. Pentru un t fixat si un xi fixat, putem calcula în O(N) profitul adus.
Numărul de operaţii făcute de program o să fie N * (fr1 * N + fr2 * N + … + frK * N) = N * (fr1 + fr2 + … + frK) * N = N3 operaţii, rezultă complexitate O(N3).

Soluţie în O(N2*logN + N*K*logN) - 40 puncte:

Putem optimiza soluţia de mai sus modificând verificarea pentru un t fixat. Calculăm un vector buck unde bucki = numărul de clienţi satisfăcuţi în bucketul i (intervalul [i * t, i * t + t) ) dacă am avea un număr infinit de alimente. Dacă ne fixăm un x, atunci numărul de porţii servite va fi min(x, buck1) + min(x, buck2) + … + min(x, buckN / t) porţii. Putem calcula vectorul bucki foarte uşor folosind sume parţiale. Numărul de bucketuri pentru un t fixat va fi N / t, deci vectorul buck se calculează în O(N / t). Deoarece calculăm profitul maxim pentru fiecare aliment separat, vectorul buck va fi calculat de K ori, deci pentru calculele intermediare unde avem un t fixat o să facem N * K / t operaţii, deci în total o să facem N * K / 1 + N * K / 2 + … + N * K / N = N * K * (1 / 1 + 1 / 2 + … + 1 / N) = N * K * log N calcule intermediare.
Pentru un aliment i şi pentru un t fixat, iterăm prin toate valorile posibile ale lui xi şi calculăm profitul maxim în O(N / t), deci numărul de operaţii pentru a calcula profitul maxim fără calculele intermediare o să fie N * (1 / 1 + 1 / 2 + … + 1 / N) * (fr1 + fr2 + … + frK) = N2 * logN.
Complexitatea finală o să fie O(N2*logN + N*K*logN).

Soluţie în O(N*K*log2N) - 70 puncte

Un alt lucru care ne deranjează este faptul că verificăm multe valori irelevante ale lui xi.
Notăm cu fi(a) = profitul obţinut pentru alimentul i daca xi ar fi egal cu a. Să zicem că am calculat fi(a) unde buckl ≤ a < a + 2 ≤ buckl+1. Dacă ne uităm atent, o să vedem ca fi(a + 1) = fi(a) + C şi fi(a + 2) = fi(a + 1) + C = fi(a) + 2 * C. Astfel putem observa că graficul funcţiei fi este o linie frântă şi nu are sens să calculăm valoarea funcţiei decât vârfurile acesteia, deci în punctele buck1, buck1,... , buckN / t.
Pentru a evalua toate punctele, le sortăm, şi le parcurgem de la cel mai mic la cel mai mare. Fie i1, i2, …, iN / t indicii numerelor astfel încât buck{i1} ≤ buck{i1} ≤ … ≤ buck{iN / t}. Deoarece vrem să calculăm suma min(x, buck1) + min(x, buck2) + … + min(x, buckN / t), o să ţinem minte de fiecare dată suma pe prefixe. Dacă evaluăm indicele ix, numărul de alimente de pe care se scoate profit o să fie bucki1 + bucki1 + … + buckix + (N - x) * buckix alimente.
Complexitatea va deveni O(N*K*log2N), dar în practică se comportă mai bine, deoarece un log din complexitate este supraestimat.

Soluţie în O(N*K*logN) - 100 puncte

Dacă analizăm şi mai bine funcţia, o să observăm că este convexă, mai exact aceasta creşte până într-un punct şi după scade, ba chiar mai mult, putem afla care punct este optimul printr-o parcurgere sau chiar cu formulă. Pentru a vedea care e optimul, să zicem că am calculat numărul de alimente de pe care scoatem profit pentru punctul bucki. Verificăm ce se întâmpla dacă am mai lua un aliment în plus. Pentru toate elementele din buck care sunt mai mici, profitul total o să scadă cu cost * mai_mici şi o să crească cu profit * (N - mai_mici). Dacă ne uităm mai atent, o să vedem că pantele nu depind de vectorul buck şi ca sunt descrescătoare. Putem printr-o parcurgere să aflăm mai_mici, deci ştim că buckimai_mici o să fie punctul optim. Putem să îl aflăm cu funcţia kth_element care merge în timp liniar, deci complexitatea finală o să fie O(N*K*logN).