2056 - Popcorn

De la Universitas MediaWiki

Cerința

Cu toții știm că popcornul este o adevărată delicatesă culinară. În pregătirile tale pentru lotul de anul acesta (și pentru petrecerile de după), ai făcut comandă de N tipuri de floricele de porumb pentru microunde. Fiecare tip are asociate 3 valori:

  • A[i] = Timpul (în secunde) la care orice floricică de acel tip “pocnește”;
  • B[i] = Timpul (în secunde) la care orice floricică de acel tip “se arde”;
  • C[i] = Cantitatea (în floricele) a respectivului tip.

Mai ai la dispoziție M pungi pentru floricele de unică folosință de capacitate foarte mare (practic, infinită) și un cuptor cu microunde. Cum, bineînțeles, nimănui nu îi plac floricelele nefăcute sau cele arse, îți dorești să le partiționezi convenabil în cele M pungi și apoi să le introduci pe rând în cuptorul cu microunde, setându-i un timp de preparare prep[i] corespunzător, astfel încât după cele M tranșe să obții cât mai multe floricele comestibile.

Formal, o floricică de tipul i introdusă în punga j , setată la timpul (în secunde) de preparare prep[j] este comestibilă dacă și numai dacă A[i] ≤ prep[j] < B[i].

Fiind date cele N tipuri de floricele și numărul de pungi disponibile, trebuie să găsești o partiție convenabilă și timpii optimi de preparare pentru fiecare pungă, astfel încât la final să obții numărul maxim de floricele comestibile, pe care să îl afișezi în fișierul de ieșire. Prea ușor!

Date de intrare

Fișierul de intrare popcorn.in conține pe prima linie numerele naturale N și M , separate printr-un spațiu, cu semnificația din enunț. Pe următoarele N linii se vor afla valorile A[i] , B[i] , C[i] corespunzătoare fiecărui tip de floricele.

Date de ieșire

Pe ecran se va afișa mesajul: "Datele de intrare corespund restricțiilor impuse."

Pe următorul rând se va afișa un singur număr natural reprezentând numărul maxim de floricele comestibile care se poate obține.

În cazul în care datele introduse de la tastatură nu îndeplinesc cerințele enunțate, pe ecran se va afișa mesajul "Datele de intrare nu corespund restricțiilor impuse."

Restricții și precizări

  • 1 ≤ M ≤ N ≤ 200 000
  • 1 ≤ A[i] < B[i] ≤ 200 000
  • Numărul total de floricele nu depășește 109
  • Unele pungi pot fi goale !
  • X = max{N, B[1], B[2], …, B[N]}

Exemplu 1

Intrare
5 2
2 4 3
1 5 6
4 8 10
7 8 2
10 11 2
Ieșire
Datele de intrare corespund restricțiilor impuse.
21

Explicație

Avem 5 tipuri de floricele și 2 pungi disponibile.

Una din soluțiile posibile este:

  • Punga 1 va conține tipurile 1 și 2 și va fi preparată la timpul 3.
  • Punga 2 va conține tipurile 3, 4, 5 și va fi preparată la timpul 7.

Toate tipurile de floricele vor fi preparate cu succes, în afară de tipul 5, care vor rămâne în stadiul de boabe.

Exemplu 2

Intrare
3 3
1 2 2
2 3 3
1 3 5
Ieșire
Datele de intrare corespund restricțiilor impuse.
10

Explicație

Putem alege pungile astfel:

  • Punga 1 va conține tipurile 1, 3 și va fi preparată la timpul 1.
  • Punga 2 va conține tipul 2 și va fi preparată la timpul 2.
  • Punga 3 va fi goală.

Exemplu 3

Intrare
3 3
2 1 2
3 2 3
1 3 5
Ieșire
Datele de intrare nu corespund restricțiilor impuse.

Rezolvare

#2056 Popcorn
T_POCNIRE = 0
T_ARDERE = 1
CANTITATE = 2


def conditii(n_tipuri, n_pungi, tipuri_floricele):
    for tip in tipuri_floricele:
        if not 1 <= tip[T_POCNIRE] < tip[T_ARDERE] <= 200_000:
            return False

    if sum([tip[CANTITATE] for tip in tipuri_floricele]) > 10**9:
        return False

    if not 1 <= n_pungi <= n_tipuri <= 200_000:
        return False

    x = [tip[T_ARDERE] for tip in tipuri_floricele]
    x.append(n_tipuri)

    x_max = max(x)

    if not n_pungi <= x_max <= 50_000:
        return False

    return True


def popcorn(n_tipuri, n_pungi, floricele):
    # Inițializăm o listă bidimensională pentru a stoca cantitatea de popcorn care poate fi preparată pentru fiecare
    # tip de popcorn și pentru fiecare pas de timp

    # Lista are n_tipuri rânduri și 50,000 coloane, deoarece fiecare tip de popcorn poate fi preparat pentru maxim
    # 50,000 de pași de timp
    nr = [[0 for j in range(50_000)] for i in range(n_tipuri)]
    for i in range(n_tipuri):
        # Pentru fiecare tip de popcorn, parcurgem pașii de timp când popcorn-ul poate fi preparat
        # (de la T_POCNIRE la T_ARDERE)
        for k in range(floricele[i][T_POCNIRE], floricele[i][T_ARDERE]):
            if k < floricele[i][T_ARDERE]:
                nr[i][k] = floricele[i][CANTITATE]

    # Inițializăm o altă listă bidimensională cu n_tipuri+1 rânduri și n_pungi+1 coloane
    # Fiecare element din listă va stoca cantitatea maximă de popcorn care poate fi pusă în j pungi
    # folosind primele i tipuri de popcorn
    dinamic = [[0 for j in range(n_pungi+1)] for i in range(n_tipuri+1)]
    for i in range(1, n_tipuri+1):
        for j in range(1, n_pungi+1):
            for k in range(j, -1, -1):
                # Calculăm cantitatea maximă de popcorn care poate fi pusă în j pungi folosind primele i tipuri de popcorn,
                # bazat pe cantitatea de popcorn care poate fi preparată pentru fiecare tip și pas de timp, stocată în lista "nr"
                dinamic[i][j] = max(dinamic[i][j], dinamic[i-1][k] + sum(nr[i-1][:j-k]))

    # Printăm cantitatea maximă de popcorn care poate fi pusă în pungi, 
    # care este valoarea din colțul din dreapta jos al tabelei "dinamic"
    print(max(dinamic[-1]))


if __name__ == "__main__":
    n_tipuri, n_pungi = [int(x) for x in input().split(" ")]

    floricele = []

    for i in range(n_tipuri):
        timp_pana_la_pocnire, timp_pana_la_ardere, cantitate = [int(x) for x in input().split()]
        floricele.append([timp_pana_la_pocnire, timp_pana_la_ardere, cantitate])

    if not conditii(n_tipuri, n_pungi, floricele):
        print("Datele de intrare nu corespund restricțiilor impuse.")
    else:
        print("Datele de intrare corespund restricțiilor impuse.")
        popcorn(n_tipuri, n_pungi, floricele)