OIS 2016 Server

Questo problema è più difficile di quello che sembra.
La mia idea era di partire dalla fine, vedere quanti utenti fossero connessi e calcolare quanto venisse speso fino alla disconnessione dell’ultimo utente, partire dal momento della disconnessione per cercare il costo di quello prima e così via, fino a K volte.
Però mi sono reso conto che non funziona così, perché seguendo questa strategia nel primo esempio avrei come consumo minimo 11 (mentre il risultato è 9):
Avrei gli upgrade a t=0(+2) avendo 8+3 = 11…

Come potrei risolvere questo problema? Qualche consiglio?
(Avevo pensato alla programmazione dinamica)

La programmazione dinamica è la strada giusta, prova a calcolare per ogni utente che si connette qual è la spesa minima che riesci a sostenere per ogni possibile K, dovresti capire come riutilizzare le soluzioni precedenti :slight_smile:

2 Mi Piace

Io avevo pensato a una soluzione diversa.

Alla fin fine, se immaginiamo il problema in via geometrica, si tratta di “diminuire gli scalini”, e la potenza totale è l’area della figura. Ovvero, se il problema presenta 3 utenti che si connettono, e quindi 3 scalini, ma posso fare solo 2 upgrade, tento di eliminare uno scalino. Quale? Quello che mi aggiunge meno area possibile. Ovviamente devono essere scelti due scalini consecutivi, e facendo in modo che l’area aggiunta sia la minore possibile.

Una soluzione del genere è greedy, e non sempre le soluzioni greedy riescono a trovare la soluzione minima assoluta.

Nel caso in cui K = 2 e N = 3 sì, è intuitivo vedere che conviene fare la scelta greedy (in generale quando K = N - 1). Nel caso più generale però non è ovvio. Magari c’è in caso in cui, aumentando K di 1, l’insieme dei gradini da scegliere cambia completamente…

Se ho N= 7 e K=2, il ragionamento è ancora possibile.
Prima elimino uno scalino, poi ne elimino un’altro, poi un’altro ancora finché gli scalini rimasti non sono due. Poi calcolo l’area.

Unico dubbio che mi rimane è la complessità

1 Mi Piace

Sì il ragionamento è possibile ma non è ovvio vedere che l’area finale (dopo aver rimosso uno scalino alla volta) sia la minima possibile. Il punto è (ed è più o meno lo stesso per tutte le soluzioni greedy): cosa ti assicura che non ci sia un modo diverso di rimuovere gli scalini che ti permette di ottenere un’area ancora minore?

2 Mi Piace

Con i giusti accorgimenti nel calcolare l’area che si aggiunge per ogni scalino probabilmente la complessità riesce ad essere minore dell’approccio dinamico, che però dovrebbe stare comunque dentro i tempi considerando le dimensioni ridotte dell’input.
Inoltre come diceva @wil93, siccome “togliere uno scalino” modifica anche il valore necessario per rimuovere gli scalini adiacenti, non sempre la scelta migliore ad ogni passaggio ti garantisce la soluzione migliore complessivamente.

1 Mi Piace

Riesci a trovare in controesempio?

Così su due piedi no :sweat_smile:

Un modo per trovarlo può essere questo: una volta scritta la soluzione greedy, si generano tanti input (magari molto piccoli) e si confrontano gli output prodotti dalle due soluzioni. Se ti va di scrivere la greedy posso trovare un controesempio in questo modo :smile: anche se immagino che una volta scritta la greedy basta mandarla al correttore per vedere se è ottima o no :sweat_smile:

O magari invece è ottima e in tal caso non ci sono controesempi :slight_smile:

Ora che ci penso, la soluzione greedy non è difficile da “eseguire a mano”. Posso provare a generare tipo 10 casi piccoli e risolverli a mano e con la soluzione ottima, e vedere se trovo una differenza. Torno subito :wink:

Ho provato ad implementare la soluzione di @erixstep e caricandola sul correttore fallisce i testcase 12, 14 e 15, prendendo 70/100.
Ora mi metto alla ricerca di un controesempio :grin:

1 Mi Piace

29 3
5
0 7 11 14 19

Risposta Greedy: 104

Risposta Corretta: 103

11 Mi Piace

Ho provato a risolverlo con la programmazione dinamica, parto dall’ultimo elemento e quando posso cerco di diminuire il carico del server, però ottengo solo 45/100 (i timeout sono diventati non corretti, tranne uno che è corretto)

Questo è il codice, l’ho anche commentato:

http://pastebin.com/FMJ2WktD

EDIT: Ho sistemato anche nel codice, avevo dimenticato sizeof(int) nel memset, però ottengo due timeout, l’ultimo testcase del subtask 2 e del subtask 4