Salta al contenuto
Note per Studenti Esercizio 15 · ordini con scadenza e profitto massimo

Esercizio 15ordini con scadenza e profitto massimo

In questa pagina 5

Testo (scritto del 16/06/2025, seconda parte, esercizio 2, 6 punti). Gestisci un'azienda che produce circuiti elettronici customizzati. Hai una serie di ordini, ciascuno caratterizzato da tre parametri: id, scadenza (in giorni) e profitto. Ogni circuito richiede un giorno per essere completato. Gli ordini che hai sono troppi per riuscire a rispettare tutte le scadenze: devi quindi rinunciare ad alcuni ordini. Vuoi però tenere quelli che ti garantiscono il maggior profitto, rispettando al tempo stesso le scadenze. Istanza di esempio (id: scadenza, profitto):

(1:4,70)(1: 4, 70), (2:2,60)(2: 2, 60), (3:4,50)(3: 4, 50), (4:3,40)(4: 3, 40), (5:1,30)(5: 1, 30), (6:4,20)(6: 4, 20), (7:6,90)(7: 6, 90), (8:2,10)(8: 2, 10), (9:3,80)(9: 3, 80), (10:1,20)(10: 1, 20), (11:5,100)(11: 5, 100), (12:6,30)(12: 6, 30), (13:2,55)(13: 2, 55), (14:5,35)(14: 5, 35), (15:3,25)(15: 3, 25).

Gli ordini con scadenza 11 vanno consegnati domani, quelli con scadenza 22 dopodomani e così via. In sei giorni, però, potrai produrre e consegnare solo 66 circuiti, uno al giorno. Quali sono i 66 ordini che ti garantiscono il maggior profitto rispettando le scadenze? (Di quelli con scadenza 11 potrai sceglierne al massimo 11, di quelli con scadenza 22 al massimo 22 sacrificando quelli con scadenza 11 se poco redditizi, e così via.) Descrivere un algoritmo iterativo in pseudocodice che, data una qualsiasi lista di ordini, restituisca la lista di ordini che garantiscono il maggior profitto rispettando le scadenze. Il punteggio pieno si otterrà con un algoritmo di complessità al caso pessimo ∈O(nlog⁡n)\in O(n \log n). Consigli: si può assumere la lista ordinata per scadenza; usare una coda con priorità con il profitto come chiave; scegliere tra coda sul minimo o sul massimo.

(a) Descrivere a parole l'idea, con l'istanza di esempio. (b) Specificare firma, input e output. (c) Scrivere l'algoritmo in pseudocodice. (d) Analizzare la complessità al caso pessimo scegliendo una implementazione della coda con priorità.


(a) Idea

Un insieme SS di ordini è realizzabile se per ogni t≥1t \ge 1 il numero di ordini di SS con scadenza ≤t\le t è al più tt (in tt giorni si producono al più tt circuiti). Si scorrono gli ordini per scadenza crescente tenendo l'insieme SS dei migliori finora, in una coda con priorità sul minimo (chiave = profitto): per ogni nuovo ordine con scadenza dd lo si inserisce; se ora ∣S∣>d\lvert S \rvert > d la scadenza non è rispettabile, e si elimina l'ordine meno redditizio (removeMin) tra quelli in SS, compreso il nuovo.

Perché la coda sul minimo: serve estrarre il profitto più basso, non il più alto. Perché funziona: gli ordini sono visti per scadenza crescente, quindi il nuovo ordine ha scadenza ≥\ge tutti quelli in SS e l'unico vincolo che può essere violato è ∣S∣≤d\lvert S \rvert \le d. Quando succede, togliere l'ordine di profitto minimo lascia un insieme realizzabile e di profitto massimo tra quelli di quella taglia (scambiando un ordine scelto con uno non scelto più redditizio non peggiora il risultato).

Traccia sull'istanza (ordinata per scadenza, scritti i profitti nello heap; dopo l'elaborazione di ciascun ordine):

Ordine (d,p)(d, p) Heap dopo inserimento ∣S∣\lvert S \rvert vs dd Azione Heap finale del passo
55: (1,30)(1, 30) {30}\{30\} 1≤11 \le 1 — {30}\{30\}
1010: (1,20)(1, 20) {20,30}\{20, 30\} 2>12 > 1 togli 2020 {30}\{30\}
22: (2,60)(2, 60) {30,60}\{30, 60\} 2≤22 \le 2 — {30,60}\{30, 60\}
88: (2,10)(2, 10) {10,30,60}\{10, 30, 60\} 3>23 > 2 togli 1010 {30,60}\{30, 60\}
1313: (2,55)(2, 55) {30,55,60}\{30, 55, 60\} 3>23 > 2 togli 3030 {55,60}\{55, 60\}
44: (3,40)(3, 40) {40,55,60}\{40, 55, 60\} 3≤33 \le 3 — {40,55,60}\{40, 55, 60\}
99: (3,80)(3, 80) {40,55,60,80}\{40, 55, 60, 80\} 4>34 > 3 togli 4040 {55,60,80}\{55, 60, 80\}
1515: (3,25)(3, 25) {25,55,60,80}\{25, 55, 60, 80\} 4>34 > 3 togli 2525 {55,60,80}\{55, 60, 80\}
11: (4,70)(4, 70) {55,60,70,80}\{55, 60, 70, 80\} 4≤44 \le 4 — {55,60,70,80}\{55, 60, 70, 80\}
33: (4,50)(4, 50) +50+ 50 5>45 > 4 togli 5050 {55,60,70,80}\{55, 60, 70, 80\}
66: (4,20)(4, 20) +20+ 20 5>45 > 4 togli 2020 {55,60,70,80}\{55, 60, 70, 80\}
1111: (5,100)(5, 100) {55,60,70,80,100}\{55, 60, 70, 80, 100\} 5≤55 \le 5 — {55,60,70,80,100}\{55, 60, 70, 80, 100\}
1414: (5,35)(5, 35) +35+ 35 6>56 > 5 togli 3535 invariato
77: (6,90)(6, 90) {55,60,70,80,90,100}\{55, 60, 70, 80, 90, 100\} 6≤66 \le 6 — {55,60,70,80,90,100}\{55, 60, 70, 80, 90, 100\}
1212: (6,30)(6, 30) +30+ 30 7>67 > 6 togli 3030 invariato

Risultato: gli ordini 13,2,1,9,11,713, 2, 1, 9, 11, 7 con profitto totale 55+60+70+80+100+90=45555 + 60 + 70 + 80 + 100 + 90 = \mathbf{455}. Un calendario valido: giorno 11: ordine 1313; giorno 22: ordine 22; giorno 33: 99; giorno 44: 11; giorno 55: 1111; giorno 66: 77 (ciascuno entro la sua scadenza). Il valore 455455 coincide con il massimo ottenuto provando tutti i 2152^{15} sottoinsiemi (controllato con un programma, e l'algoritmo è stato confrontato con la ricerca esaustiva su istanze casuali).

(b) Firma

Algoritmo ordiniMigliori(ordini, n)
Input: array ordini[1..n]; ogni ordine ha id, scadenza (intero >= 1), profitto
Output: lista degli ordini scelti (realizzabili e di profitto massimo)

(c) Pseudocodice

ordina ordini per scadenza crescente
Q <- coda con priorità sul minimo, vuota          (chiave = profitto, valore = ordine)
for each o in ordini (nell'ordine) do
    Q.insert(o.profitto, o)
    if Q.size() > o.scadenza then
        Q.removeMin()                              (l'ordine meno redditizio tra quelli tenuti)
return la lista dei valori in Q

(d) Complessità

Complessità totale O(nlog⁡n)O(n \log n) al caso pessimo. Con una coda su lista non ordinata ogni removeMin costerebbe Θ(n)\Theta(n): O(n2)O(n^2) (vedi Code con prioritàEntry chiave-valore; ADT coda con priorità (insert, min, removeMin) con chiave minima = priorità massima; esempio di esecuzione; applicazioni; implementazioni con lista non ordinata e ordinata e relativi costi; ordinamento tramite coda con priorità.Code con priorità →); con lista ordinata gli insert costerebbero Θ(n)\Theta(n): ancora O(n2)O(n^2).

Errori comuni

  • Usare una coda sul massimo: si estrarrebbe l'ordine più redditizio, cioè proprio quello da tenere.
  • Scorrere gli ordini per scadenza decrescente o senza ordine: il controllo ∣S∣>d\lvert S \rvert > d perde di significato.
  • Confrontare con la scadenza il numero di giorni trascorsi invece della dimensione dell'insieme tenuto.
  • Scegliere i 66 ordini di profitto massimo ignorando le scadenze (nell'esempio: 100,90,80,70,60,55100, 90, 80, 70, 60, 55 sono proprio quelli giusti, ma solo perché le scadenze lo permettono; in generale non basta).

Versione ripasso

Testo. Ordini (id, scadenza, profitto), un circuito al giorno; scegliere gli ordini che rispettano le scadenze con profitto massimo, in O(nlog⁡n)O(n \log n), usando una coda con priorità (sul minimo o sul massimo?).

Teoria collegata