I_R_W14_M42_C++ Ciekawe algorytmy rekurencyjne
Problem ośmiu hetmanów — notacja
Problem ośmiu hetmanów polega na ustawieniu ośmiu figur szachowych znanych jako hetman lub królowa na klasycznej szachownicy o wymiarach 8x8 w taki sposób, aby nie atakowały się wzajemnie. Hetman to figura, która może poruszać się wzdłuż kolumn, wzdłuż wierszy, a także po skosie, zbijając przy tym inne figury, które stoją na jej drodze. Chcąc ustawić ośmiu hetmanów tak, by nie zbijały się nawzajem, nie można dopuścić do sytuacji, w której dwa z nich znajdą się w jednym wierszu lub w jednej kolumnie. Warunkiem koniecznymWarunkiem koniecznym (lecz nie wystarczającymwystarczającym) jest więc to, aby każda z ośmiu figur była ustawiona w innej kolumnie – w przeciwnym wypadku przynajmniej dwie figury atakowałyby się wzajemnie. Notacja stosowana do przedstawienia rozwiązania problemu ośmiu hetmanów składa się z ośmiu liczb, z których każda i-ta liczba oznacza wiersz, w którym znajduje się hetman z i-tej kolumny. Pierwsza liczba stanowi zatem numer wiersza, w którym znajduje się hetman z pierwszej kolumny; druga — numer wiersza, w którym znajduje się hetman z drugiej kolumny, itd.
Implementacja w języku C++
Na początek stwórzmy globalną tablicę typu całkowitego o długości 9. Będzie ona zawierała numery wierszy hetmanów na odpowiednich pozycjach. Komórka o indeksie 0 nie będzie używana.
Teraz zdefiniujmy funkcję o nazwie hetman, przyjmującą parametr będący numerem wiersza, w którym chcemy postawić hetmana. Wewnątrz funkcji stworzymy pętlę, która będzie iterować po wszystkich kolumnach i sprawdzać, czy któraś z nich jest wolna.
Jeżeli istnieje wolna kolumna, musimy sprawdzić, czy pole w tym wierszu i w tej kolumnie nie jest atakowane przez innego hetmana po skosie. Tworzymy pętlę, w której będziemy sprawdzać, czy istnieje hetman w innej kolumnie – a jeśli tak, to czy jego zakres ruchu nie zagraża pozycji naszego nowego hetmana. W tym celu używamy wzoru:
gdzie to współrzędne pierwszego hetmana, zaś to współrzędne drugiego hetmana.
Jeżeli pole jest atakowane, ustawiamy odpowiednią zmienną na 1. Następnie, jeśli mamy pewność, że pole nie jest zagrożone, umieszczamy hetmana na odpowiedniej pozycji, zapisując numer wiersza w komórce tablicy o indeksie równym numerowi kolumny.
Jeśli nie ustawiliśmy jeszcze wszystkich hetmanów, przechodzimy do kolejnego wiersza, wywołując rekurencyjnierekurencyjnie funkcję hetman(wiersz + 1). W momencie zaś, gdy wszystkie osiem wierszy zostanie wypełnione, wypisujemy całe rozwiązanie na standardowe wyjście.
Jeżeli jednak w danym wierszu nie znajdziemy pola, którego nie atakuje inny hetman, musimy powrócić do poprzedniego wiersza i dla kolumny, w której się znajdował, ustawić wartość 0. Dzięki wcześniejszemu zastosowaniu pętli zostanie wyszukane dla niego inne wolne pole. Jeżeli tu także nie znajdziemy innego wolnego pola, nastąpi ponowne cofnięcie itd. Algorytm taki nazywamy algorytmem z nawrotamialgorytmem z nawrotami.
Teraz wystarczy w głównej funkcji wypełnić komórki tablicy wartością 0 i wywołać funkcję hetman dla pierwszego wiersza. Oto gotowy kod naszego programu, odnajdujący wszystkie możliwe rozwiązania problemu ośmiu hetmanów w języku C++:
Problem n hetmanów
Problem n hetmanów to termin, którym określamy zastosowanie problemu ośmiu hetmanów dla szachownicy mniejszej lub większej niż standardowa (8x8 pól). W problemie n hetmanów chcemy ustawić n figur na szachownicy o wymiarach nxn w taki sposób, aby nie atakowały się wzajemnie.
Poniższa tabela prezentuje liczbę rozwiązań problemu n hetmanów dla danych wartości n:
n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
liczba rozwiązań | 1 | 0 | 0 | 2 | 10 | 4 | 40 | 92 | 352 | 724 | 2680 | 14200 |
Korzystając z zaprezentowanego programu, dokonaj jego uogólnienia i napisz program, który rozwiąże problem dla n hetmanów.
Słownik
warunek (zazwyczaj jeden z wielu), którego spełnienie jest konieczne dla zaistnienia danego faktu
warunek, którego spełnienie wystarcza dla zaistnienia danego faktu
technika programowania, w której funkcja odwołuje się do samej siebie
algorytm, w którym zapisujemy kolejne kroki rozwiązania; gdy okaże się, że dany krok nie prowadzi do prawidłowego rozwiązania całego problemu, następuje powrót do poprzedniego etapu i szukanie innego wyjścia