Esercizio 12
Scrivere un programma che consenta di disporre otto regine su una scacchiera di 8x8 celle senza che nessuna metta sotto scacco un'altra .
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 8x8 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
}