/* Fili e interruttori (Wires and Switches, IOI 1995) Copyright (C) 2002 Paolo Boldi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Questo programma usa una strategia divide-et-impera. Prova tutti i fili con la prima metà degli interruttori chiusi. A questo punto, sui soli fili che hanno dato luogo ad un'accensione della lampadina dovrà vedere in quale dei primi due quarti si trova l'interruttore corrispondente. Per i rimanenti, dovrà guardare negli ultimi due quarti. Notate che quando si vuole provare un filo di cui si sa già che l'interruttore corrispondente sta in x, x+1, ..., y e si vuole vedere se sta in x, x+1, ...., (x+y)/2 oppure in (x+y)/2+1, ..., y occorre cambiare lo stato di alcuni interruttori fra x e y, ma non è importante cambiare lo stato degli interruttori fuori da questo intervallo perché essi sono ininfluenti. Il caso peggiore per questo algoritmo è quando ogni interruttore è collegato a un filo diverso: anche in questo caso, il programma richiede meno di 900 domande. NOTA: interruttori e fili sono internamente numerati da 0 a m-1. */ #include #define MAXFILI 90 /* Attualmente sono chiusi gli interruttori curx, curx+1, ..., cury */ int curx, cury; /* dec[i] è il numero dell'interruttore a cui il filo i è collegato */ int dec[MAXFILI]; /* Legge una risposta (Y/N) e restituisce 1 se la risposta è Y, 0 altrimenti */ int leggirisp() { char c; while ( ( c = getchar() ) != 'Y' && c != 'N' ); return ( c == 'Y' ); } /* Questa funzione garantisce che, dopo averla chiamata, siano chiusi gli interruttori da cx a cy, e aperti gli interruttori da x a y che non compaiono nel precedente insieme. Cioè, detto A = {cx,cx+1,...,cy} B = {x,x+1,...,y} saranno chiusi gli interruttori in A, e aperti gli interruttori in B-A. NOTA: dopo la chiamata alla funzione, i valori curx, cury saranno corretti. */ void chiudi( int cx, int cy, int x, int y ) { int i; for ( i = x; i <= y; i++ ) { if ( ( cx <= i && i <= cy ) != ( curx <= i && i <= cury ) ) { printf( "C %d\n", i+1 ); fflush( stdout ); leggirisp(); } } curx = cx; cury = cy; } /* Fa il test sul filo i, e restituisce 1 se la lampadina si è accesa, 0 altrimenti */ int test( int i ) { printf( "T %d\n", i+1 ); fflush( stdout ); return leggirisp(); } /* Questa funzione riceve un array filo[0],...,filo[nfili-1] di numeri di fili che sono garantiti essere collegati ad un interruttora da x a y. Dopo averla chiamata, i fili saranno tutti decisi. */ void decidi( int filo[], int nfili, int x, int y ) { int i, mezzo, pm, sm; int primameta[MAXFILI], secondameta[MAXFILI]; if ( nfili == 0 ) return; /* Non c'è nessun filo */ if ( x == y ) { /* L'intervallo contiene un solo interruttore */ for ( i = 0; i < nfili; i++ ) dec[filo[i]] = x; return; } mezzo = ( x + y ) / 2; chiudi( x, mezzo, x, y ); pm = sm = 0; for ( i = 0; i < nfili; i++ ) if ( test( filo[i] ) ) primameta[pm++] = filo[i]; else secondameta[sm++] = filo[i]; decidi( primameta, pm, x, mezzo ); decidi( secondameta, sm, mezzo+1, y ); } int main() { int i, m; int tutti[MAXFILI]; scanf( "%d", &m ); for ( i = 0; i < m; i++ ) tutti[i] = i; curx = cury = -1; decidi( tutti, m, 0, m-1 ); printf( "D" ); for ( i = 0; i < m; i++ ) printf( " %d", dec[i]+1 ); printf( "\n" ); fflush( stdout ); return 0; }