Esercizio No. 12 Funzioni
Scrivere un programma che consenta di disporre otto regine su una scacchiera di 8×8 celle senza che nessuna metta sotto scacco un’altra.
Soluzione:
Gli algoritmi di backtracking, sono algoritmi che trovano la soluzione di specifici problemi, senza seguire una regola computazionale fissa; essi procedono per tentativi.
Generalmente si decompone il procedimento in vari obiettivi parziali come nel caso degli algoritmi di ricerca , inoltre si fa uso di funzioni ricorsive, cioè, funzioni che invocano loro stesse; nel caso il tentativo effettuato non sia andato a buon fine.
Il problema delle otto regine è un classico esempio di uso dei metodi per tentativi.
Fu studiato, ma non completamente risolto da Gauss nel 1850, anche perchè la caratteristica di questi problemi è che ‘sfuggono’ a soluzioni analitiche.
In breve: otto regine devono essere disposte su una scacchiera di 8×8 celle senza che nessuna metta sotto scacco un’altra.
Scegliamo una opportuna rappresentazione dei dati, poiché è noto dalle regole degli scacchi che una regina può mangiare tutti i pezzi che si trovano nella sua colonna, nella sua riga o su una delle sue diagonali, si deduce che ogni colonna potrà contenere una ed una sola regina.
Dovendo operare con 8 regine, 8 righe ed 8 colonne, è pressoché obbligatorio dichiarare la costante
const int n = 8;
Per controllare se una regina non si trova sotto scacco di altre già precedentemente piazzate si elabora la seguente funzione
bool sicura(int regina, int riga){
for (int i = 0; i < regina; i++){
// ricava la posizione della regina nella riga iesima
int xriga = T[i];
//stessa riga o stessa diagonale
if (xriga == riga || xriga == riga – (regina – i) ||
xriga == riga + (regina – i))
return false; }
return true;
}//fine sicura()
int regina è una variabile che identifica la k-esima regina delle 8 totali
int riga è il valore della riga dove si è deciso di piazzare la regina.
Se la regina si trova sotto scacco viene restituito false altrimenti true e il pezzo viene considerato in una posizione sicura.
Il programma procede per colonne su ognuna delle quali viene disposta una regina, la riga viene individuata dalla seguente funzione:
void tenta(int k){
if (k < n){
for (int i = 0; i < n; i++)
if (sicura(k, i)){ T[k] = i; tenta(k + 1); }//fine if
}else{
for (int i = 0; i < n; i++) cout << T[i] ;
cout << endl;
}//fine else
}//fine tenta()
Viene usato il vettore T[k] che memorizza la colonna dove si trova la k-esima regina, poi per tentativi si esplorano tutte le righe dalla 0 alla 7 ( n-1 ) dove sia possibile posizionare il pezzo.
Se il pezzo finisce in una posizione sicura, si passa alla regina successiva con l’istruzione
tenta(k + 1);
ovviamente ci sarà una regina per ogni colonna ( n=8 ) l’intera procedura viene avviata dal main() con l’istruzione
tenta(0);
e con 0 si intende la regina con indice 0 in colonna 0.
Per ogni colonna saranno possibili diverse combinazioni qui rappresentiamo solo le prime 10 soluzioni
0 4 7 5 2 6 1 3
0 5 7 2 6 3 1 4
0 6 3 5 7 1 4 2
0 6 4 7 1 3 5 2
1 3 5 7 2 0 6 4
1 4 6 0 2 7 5 3
1 4 6 3 0 7 5 2
1 5 0 6 3 7 2 4
se consideriamo la prima soluzione che parte dalla colonna 0:
Una soluzione ovvia, sarebbe stata quella di rappresentare la scacchiera con una matrice quadrata; ma un esame più attento rivela che questa rappresentazione rende più difficile controllare la disponibilità delle posizioni. Considerando che questa operazione è quella più frequentemente eseguita è stato necessario scegliere una rappresentazione che permetta di verificare le posizioni nel modo più semplice possibile.
#include < iostream >
using namespace std;
const int n = 8;
int T[n];//vettore rappresentativo delle 8 righe
// Controlla se la posizione è sicura
bool sicura(int regina, int riga){
// Controlla se ogni regina precedente alla attuale
// non si trovi sulla stessa riga o su una delle diagonali
for (int i = 0; i < regina; i++){
// ricava la posizione della regina nella riga iesima
int xriga = T[i];
// stessa riga o stessa diagonale
if (xriga==riga ||
xriga == riga – (regina – i) ||
xriga == riga + (regina – i))
return false;
}
return true;
}//fine sicura()
void tenta(int k){
if (k < n){
for (int i = 0; i < n; i++)
// prima di inserire la k-esima regina in una riga
// controlla se la posizione scelta è sicura
if (sicura(k, i)){
T[k] = i;
tenta(k + 1);// posiziona un’altra regina
}//fine if
}else{
// le n=8 regine sono posizionate (stampa)
for (int i = 0; i < n; i++) cout << T[i] << ” “;
cout << endl;
}//fine else
}//fine tenta
main(){
tenta(0);//regina iniziale in colonna 0
}
Commento all'articolo