Tournament Planning(Poker)


#1

Ottengo 10/100…
Le subtask falliscono principalmente per output non corretti e, in parte, anche per execution timed out

#include<iostream>
#include<vector>
#include<fstream>
#include<algorithm>
using namespace std;

    ifstream leggi("input.txt");ofstream scrivi("output.txt");
    int N,M;
    int Dtemp,temp;
    vector<int>D;
    int u,v;
    
    struct poker{
    	int S,E,B,P;
	};
	
	bool sortByEnd(const poker &a,const poker &b)
	{
		return(a.E<b.E);
	}
	
	int maxVal(const int &a, const int &b)
	{
		return (a>b?a:b);
	}
	
	int max_win(poker match[],int u,int v){
		sort(match+u,match+v+1,sortByEnd);
		int lim=v-u+2;//indica la dimensione delle righe della matrice, corrisponde al numero di match 
		int m[match[v].E+1][lim];
		for(int i=0;i<=match[v].E;i++)
		{
			m[i][0]=0;
			
//			cout<<i<<") "<<m[i][0]<<" ";
			for(int k=1;k<lim;k++){//una colonna sarà sempre inizializzata a 0
				
				if((match[u+k].E<=i)&&(M+m[match[u+k].S][k-1]>=match[u+k].B))
				{
					m[i][k]=maxVal(m[i][k-1],m[match[u+k].S][k-1]+(match[u+k].P-match[u+k].B));
				}else{
					m[i][k]=m[i][k-1];
				}
//				cout<<m[i][k]<<" ";
			}
//			cout<<"\n";
		}
		return m[match[v].E][lim-1];
	}
int main(){
    leggi>>N>>M;
	poker match[N];
    Dtemp=-1;
    poker *x;
    for(int i=0;i<N;i++)
    {
    	x=&match[i];
        leggi>>temp;
        leggi>>x->S>>x->E>>x->B>>x->P;
        if(Dtemp==temp){
            D.back()++;
        }else
        {
		    Dtemp=temp;
	        D.push_back(1);
        }      
    }
    u=0;v=-1;
    for(int i=0;i<D.size();i++)
	{
		u=v+1;
		v=u+D.at(i)-1;
    	if(D.at(i)==1){
    		if(M>=match[u].B)
    			M+=(match[u].P-match[u].B);
		}else{
			M+=max_win(match,u,v);
		}

	}
	scrivi<<M;
       
    
}

#2

Che approccio mi consigliate di utilizzare per risolvere il problema Tournament Planning?
link: https://training.olinfo.it/#/task/ois_poker/statement
Ho provato a risolverlo con la bottom up ma ottengo solo 10/100…
Qui ho postato il mio codice: Tournament Planning(Poker)


#3

L’ approccio da usare è la programmazione dinamica.
Prova a spiegare l’ idea che hai in mente e quello che fa il codice.
Leggendolo non si capisce che fa :sweat_smile:


#4

Mi consiglieresti di utilizzare la top down o la bottom up?
In pratica cerco di utilizzare la bottom up, almeno credo :sweat_smile: .
Le variabili u e v rappresentano la posizione nel vettore “match” della prima e dell’ultima partita partita svolta nello stesso giorno.
Ogni volta che in un giorno ci sono più di una partita la funzione “max_win” verrà chiamata.
All’interno della funzione la prima cosa che faccio è ordinare la parte del vettore “match” corrispondente alle partite del giorno in questione basandosi sulla fine di ogni match(in ordine crescente).
Successivamente creo una matrice di match[v].E+1 righe, cioè in questo modo ci saranno 0,1,2… m[v].E righe(m[v].E sarebbe la partita con l’orario di fine più grande) in modo tale da poter andare a risolvere i casi in ogni limite di tempo.Mentre l’ascissa è compost dal numero di partite+1(così per ogni riga della tabella il primo valore sarà 0).
Per ogni cella della nostra tabella andrò a vedere se “giocare o non giocare la partita in questione”, nel caso in cui io decida di giocarla devo tener conto di due cose:

  1. (match[u+k].E<=i) = cioè se il match in questione finisce prima del limite di tempo i.
  2. (M+m[match[u+k].S][k-1]>=match[u+k].B) se il badget è sufficiente per giocare la partita.

Se entrambe le condizioni sono vere faccio una somma tra il guadagno di questa partita e il guadagno che potrei ottenere dall’inizio della giornata fino all’inizio della partita a cui facciamo riferimento, la somma fatta la confronto con il valore della cella alla mia sinistra(che corrisponde al caso in cui decido di non giocare la partita) e prendo il valore più grande.
Se le condizioni risultano false copio il valore della cella precedente m[i][k]=m[i][k-1].
E così “dovrei” ottenere il risultato migliore della giornata che poi, sommato con i risultati delle altre giornate e con il budget iniziale, mi dovrebbe dare il risultato finale.
Mi scuso ma purtroppo non sono molto bravo a spiegare…


#5

In bottom up è più semplice a parer mio.

Considerando il caso peggiore in cui ci sono 100 000 parite in un giorno e crei una matrice 100 000 x 1 000 hai bisogno di 100 000 000 interi che trasfromati in megabyte sono circa 400.
Quindi anche se il tuo ragionamento è giusto non prenderebbe mai il 100/100 per i limiti di memoria.
Hai bisogno di rivedere la strategia risolutiva, ordinare e dividere la dp su più giorni è già un buon inizio.
Inizia col non complicarti la vita leggendo gli input cosi, puoi salvare benissimo i valori in un vettore e quando il giorno non coincide con il precedente fai partire la dp.
Ordinando per fine (come hai fatto) ti permette di creare l’ ordine con cui potresti giocare le varie partite, quindi considerando una partita in posizione i essa può essere giocata se il budget di inizio giorno te lo permette oppure esitono delle partite j con j < i precedenti da cui potresti ricavarti la somma da poter usare nella i-esima partita.
Il budget per il giorno successivo è dato dal massimo che puoi ricavare per ogni partita e il budget iniziale di giornata.
Prova a scrivere una soluzione O(N^2) dove per ogni posizione i provi a combinarla con le precedenti.
Se tutto va bene dovresti riuscire a ottenre 85/100.
Immagino che a questo punto hai intuito che struttura usare per conservare i risultati per ogni partita.
Per il 100 su 100 ci sono due ottimizzazioni che si potrebbero fare ma l’ importante ora è arrivare ad avere output corretti.


#6

Molte grazie per il suggerimento, ero fermo da un pezzetto a 85/100 ma utilizzando le tue considerazioni sono arrivato in fondo. Tutto considerato era imparentato con la “Dieta di Poldo”.


#7

A quale complessità sei arrivato?


#8

Non ho molta esperienza con questi conti ma direi O(nlogn).
Per memorizzare il massimo che posso fare per ogni torneo e recuperare il massimo guadagno utilizzabile che precede il torneo i uso un segment tree.
Inoltre Il vettore dei tornei di una giornata viene ordinato prima della scansione.
n è il numero di tornei di una giornata.
Il tutto ripetuto per tutte le giornate.


#9

In realtà si può migliorare.
La complessità minima è data dall’ordinamento del quale non si può fare a meno, ma se invece di ordinare tutto il vettore iniziale lo si divide in giorni si possono fare N/giorni ordinamenti e la complessità è inferiore rispetto ad un unico ordinamento.
Successivamente si può notare come ordinando i tornei in base alla loro fine possiamo fare in modo di calcolare il miglior budget per ogni singolo attimo solamente una volta.
Senza ricorrere a segment o fenwick che aggiungono un fattore logaritmico.

Fabio.


#10

Questo è ciò che faccio già e anch’io ordino per fine.

Al momento non vedo chiaramente come ottenere questo senza un segment tree ma ci penserò su.
Comunque grazie per l’idea.

Vittorio.


#11

Pensala così, ogni torneo, stando a ciò che fai tu, aggiorna un suffisso dei possibili momenti, quindi , per esempio l’ultima cella del vettore viene aggiornata N volte, anche se ciò potrebbe essere fatto un’unica volta.


#12

Io direi che è più un piano degli studi in versione semplificata.


#13

Non mi pare che il mio algoritmo funzioni così, come dicevo lavora stile “La dieta di Poldo” e non è ricorsivo.
Nell’ambito dei tornei di una giornata (ordinati crescenti per orario di fine torneo):
vado dal primo fino all’ultimo
ad ogni torneo mi guardo indietro per capire se posso farlo e se si quanto al massimo mi trovo in saccoccia dopo aver vinto quel torneo, facendo attenzione a quei tornei che terminano tutti allo stesso istante e memorizzando il totale delle vincite fin li +la posta di partenza.
Il valore memorizzato ad un certo istante viene elaborato tante volte per quanti sono i tornei che terminano in quell’istante e così anche per il valore superiore dell’ultima cella.


#14

Ad intuito penso che usi il segment tree per controllare quale sia il maggior numero di soldi con il quale puoi iniziare ogni torneo.
Se è come penso:

  1. Se usi un fenwick dovrebbe essere leggermente più veloce.

  2. In questo modo per ogni torneo controlli quanto è il massimo prima del suo inizio avendo poi una complessità log(1000). Modificando leggermente l’algoritmo puoi conoscere quel valore in O(1), ma ovviamente devi cambiare l’aggiornamento che non sarà più O(1) ma O(1000) per ogni giornata:
    Quindi per ogni singolo giorno, dove hai K tornei, invece di di avere O(Klog(1000) +K) puoi avere O(2K).

Se non è come penso allora rispiega please.


#15

Si l’algoritmo lavora così.
Il suggerimento N1 mi è chiaro e ci posso provare quasi subito
Sul secondo devo meditare, tra l’altro con il calcolo della complessità ho problemi.
Un’ultima cosa:

Ma il mio aggiornamento è O(1)?

grazie per i suggerimenti


#16

Quali istruzioni esegui per aggiornare?


#17

My bad, point update nel segment ha ovviamente complessità O(logN). Sorry


#18

Un update del valore del segment tree relativamente alla fine di quel torneo.
Supponendo che:

  1. il torneo i termini al momento y
  2. che potendolo fare mi trovi in tasca z
  3. che questo z sia maggiore w cioè di quanto mi farebbe incassare un altro torneo già analizzato e che terminava sempre all’istante y (sempre che ce ne sia uno prima)
    allora vado a mettere z nel segment tree in posizione y.

Scusa ripensandoci non vedo cosa mettere dentro Fenwick a meno di ripensare il tutto.


#19

Sapendo che ogni momento y appartiene a logN intervalli l’update è in O(logN).


#20

Grazie!!! Finalmente sono riuscito ad ottenere 100/100.
Senza i tuoi “input” non credo sarei riuscito ad ottenere 100/100 :joy::joy:

Vorrei sapere come poter vedere il tempo di esecuzione del mio programma sul portale, sai se possibile/come fare?