Questo problema è un problema olimpico (IOI 1995) che oggi sarebbe considerato di difficoltà medio-bassa, ed è uno degli esempi di problemi in cui la soluzione ottimale si trova con strategia divide-et-impera. Nel resto di questo documento, spiegherò tre diverse possibili soluzioni, ma solo la terza funziona su tutti gli input.
La prima strategia, la più banale, per risolvere il problema è la seguente. Si considerano tutte le coppie filo/interruttore; per ogni coppia, si prova a chiudere l'interruttore, si testa il filo, e quindi si riapre l'interruttore.
Il caso peggiore, in questa strategia, si ha quando, per ogni filo l'interruttore corretto viene provato per ultimo. In questo caso, dovremo per forza testare tutte le m2 coppie, e per ognuna sono richieste 3 operazioni; inoltre, alla fine, ci sarà l'operazione D.
Quindi, nel caso peggiore, questa strategia richiede 3m2+1 operazioni; quindi funziona correttamente solo se 3m2+1 <= 900, cioè, m<=17.
La seconda strategia è la seguente. Si chiude l'interruttore 1, e si provano tutti i fili. Quelli che fanno accendere la lampadina sono collegati all'interruttore 1, e da questo momento in avanti non li considereremo più. Quindi si chiude l'interruttore 2, e si provano i fili rimanenti. Quelli che fanno accendere la lampadina sono collegati evidentemente all'interruttore 2, e d'ora in avanti non considereremo più nemmeno loro. Si procede così fino a quando non rimangono fili da classificare.
Il caso peggiore, per questa strategia, si ha quando tutti i fili sono collegati all'ultimo interruttore. In questo caso, tutti i fili rimarranno in gioco fino all'ultimo minuto. In pratica, le operazioni da fare in questo caso saranno:
In tutto sono quindi m-1+m(m-1)+1=m2 operazioni. Quindi, questa strategia funziona correttamente solo per m2<=900, cioè per m<=30.
La strategia che alla fine risulterà vincente è una variante della classica ricerca dicotomica: quando volete cercare un elemento in un array ordinato iniziate a confrontarlo con l'elemento mediano per scoprire se l'elemento si trovi nella prima o nella seconda metà, e proseguite così dividendo ogni volta per due lo spazio di ricerca.
In questo caso, la tecnica è analoga: prima di tutto tentiamo capire in quale metà capita l'interruttore a cui è collegato ciascun filo e poi operiamo ricorsivamente sulle due metà.
Più precisamente, il cuore del programma è costituito da una funzione decidi che prende come parametri:
Lo scopo della funzione è quello di stabilire a quale interruttore sia collegato ciascuno dei fili passati come argomento, mettendo per esempio in un array globale il numero dell'interruttore, una volta scopertolo.
Naturalmente, il programma principale chiamerà questa funzione passandole l'insieme di tutti i fili, x=1 e y=m.
La funzione opera come segue:
Ora, il modo in cui si implementa questa soluzione dipende in modo essenziale da come si implementa il punto 1. di cui sopra. Ci sono almeno due modi possibili per implementarlo:
Un'analisi del numero di operazioni richieste con la strategia dicotomica (qualunque delle sottostrategie si scelga) è piuttosto complessa, ma possiamo limitarci ad un'esame sperimentale. Occorre notare che il caso peggiore, nella strategia dicotomica, si presenta quando tutti i fili sono collegati a interruttori diversi, perché in questo caso nessuno dei rami di ricorsione si interrompe "prematuramente".
Provando a eseguire un programma che implementi la strategia dicotomica usando le sottostrategie A e B, nei casi peggiori otteniamo il seguente numero di comandi (per m=80, 81, ..., 90):
m | Sottostrategia A | Sottostrategia B |
---|---|---|
80 | 792 | 761 |
81 | 804 | 773 |
82 | 816 | 785 |
83 | 830 | 799 |
84 | 842 | 811 |
85 | 856 | 825 |
86 | 870 | 839 |
87 | 884 | 853 |
88 | 896 | 865 |
89 | 908 | 877 |
90 | 920 | 889 |
Dunque, mentre la sottostrategia A ce la fa sempre con meno di 900 operazioni, la sottostrategia B fallisce quando m>88. Per inciso, la sottostrategia B, con 91 fili, richiede 903 operazioni.
Ecco, quindi, i valori di m entro i quali funziona ciascuna strategia risolutiva:
Valori di m per cui funziona | |
---|---|
Strategia lineare | m<=17 |
Strategia lineare ottimizzata | m<=30 |
Strategia dicotomica (sottostrategia A) | m<=88 |
Strategia dicotomica (sottostrategia B) | m<=90 |