Wróć do informacji o e-podręczniku Wydrukuj Pobierz materiał do PDF Pobierz materiał do EPUB Pobierz materiał do MOBI Zaloguj się, aby dodać do ulubionych Zaloguj się, aby skopiować i edytować materiał Zaloguj się, aby udostępnić materiał Zaloguj się, aby dodać całą stronę do teczki

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 koniecznymwarunek koniecznyWarunkiem koniecznym (lecz nie wystarczającymwarunek wystarczającywystarczają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.

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int h otwórz nawias kwadratowy 9 zamknij nawias kwadratowy średnik.

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.

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int h otwórz nawias kwadratowy 9 zamknij nawias kwadratowy średnik. Linia 6. void hetman otwórz nawias okrągły int wiersz zamknij nawias okrągły otwórz nawias klamrowy. Linia 7. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny znak równości 8 średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 8. if otwórz nawias okrągły h otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości znak równości 0 zamknij nawias okrągły otwórz nawias klamrowy. Linia 9. zamknij nawias klamrowy. Linia 10. zamknij nawias klamrowy. Linia 11. zamknij nawias klamrowy.

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:

|a c| |b d|

gdzie ( a , b ) to współrzędne pierwszego hetmana, zaś ( c , d ) to współrzędne drugiego hetmana.

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int h otwórz nawias kwadratowy 9 zamknij nawias kwadratowy średnik. Linia 6. void hetman otwórz nawias okrągły int wiersz zamknij nawias okrągły otwórz nawias klamrowy. Linia 7. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny znak równości 8 średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 8. if otwórz nawias okrągły h otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości znak równości 0 zamknij nawias okrągły otwórz nawias klamrowy. Linia 9. int czyAtakowane znak równości 0 średnik. Linia 11. for otwórz nawias okrągły int j znak równości 1 średnik j otwórz nawias ostrokątny znak równości 8 średnik j plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 12. if otwórz nawias okrągły h otwórz nawias kwadratowy j zamknij nawias kwadratowy ampersant ampersant otwórz nawias okrągły abs otwórz nawias okrągły i minus j zamknij nawias okrągły znak równości znak równości abs otwórz nawias okrągły wiersz minus h otwórz nawias kwadratowy j zamknij nawias kwadratowy zamknij nawias okrągły zamknij nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 13. czyAtakowane znak równości 1 średnik. Linia 14. zamknij nawias klamrowy. Linia 15. zamknij nawias klamrowy. Linia 16. zamknij nawias klamrowy. Linia 17. zamknij nawias klamrowy. Linia 18. zamknij nawias klamrowy.

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.

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int h otwórz nawias kwadratowy 9 zamknij nawias kwadratowy średnik. Linia 6. void hetman otwórz nawias okrągły int wiersz zamknij nawias okrągły otwórz nawias klamrowy. Linia 7. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny znak równości 8 średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 8. if otwórz nawias okrągły h otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości znak równości 0 zamknij nawias okrągły otwórz nawias klamrowy. Linia 9. int czyAtakowane znak równości 0 średnik. Linia 11. for otwórz nawias okrągły int j znak równości 1 średnik j otwórz nawias ostrokątny znak równości 8 średnik j plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 12. if otwórz nawias okrągły h otwórz nawias kwadratowy j zamknij nawias kwadratowy ampersant ampersant otwórz nawias okrągły abs otwórz nawias okrągły i minus j zamknij nawias okrągły znak równości znak równości abs otwórz nawias okrągły wiersz minus h otwórz nawias kwadratowy j zamknij nawias kwadratowy zamknij nawias okrągły zamknij nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 13. czyAtakowane znak równości 1 średnik. Linia 14. zamknij nawias klamrowy. Linia 15. zamknij nawias klamrowy. Linia 17. if otwórz nawias okrągły wykrzyknik czyAtakowane zamknij nawias okrągły otwórz nawias klamrowy. Linia 18. h otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości wiersz średnik. Linia 19. zamknij nawias klamrowy. Linia 20. zamknij nawias klamrowy. Linia 21. zamknij nawias klamrowy. Linia 22. zamknij nawias klamrowy.

Jeśli nie ustawiliśmy jeszcze wszystkich hetmanów, przechodzimy do kolejnego wiersza, wywołując rekurencyjnierekurencjarekurencyjnie funkcję hetman(wiersz + 1). W momencie zaś, gdy wszystkie osiem wierszy zostanie wypełnione, wypisujemy całe rozwiązanie na standardowe wyjście.

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int h otwórz nawias kwadratowy 9 zamknij nawias kwadratowy średnik. Linia 6. void hetman otwórz nawias okrągły int wiersz zamknij nawias okrągły otwórz nawias klamrowy. Linia 7. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny znak równości 8 średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 8. if otwórz nawias okrągły h otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości znak równości 0 zamknij nawias okrągły otwórz nawias klamrowy. Linia 9. int czyAtakowane znak równości 0 średnik. Linia 11. for otwórz nawias okrągły int j znak równości 1 średnik j otwórz nawias ostrokątny znak równości 8 średnik j plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 12. if otwórz nawias okrągły h otwórz nawias kwadratowy j zamknij nawias kwadratowy ampersant ampersant otwórz nawias okrągły abs otwórz nawias okrągły i minus j zamknij nawias okrągły znak równości znak równości abs otwórz nawias okrągły wiersz minus h otwórz nawias kwadratowy j zamknij nawias kwadratowy zamknij nawias okrągły zamknij nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 13. czyAtakowane znak równości 1 średnik. Linia 14. zamknij nawias klamrowy. Linia 15. zamknij nawias klamrowy. Linia 17. if otwórz nawias okrągły wykrzyknik czyAtakowane zamknij nawias okrągły otwórz nawias klamrowy. Linia 18. h otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości wiersz średnik. Linia 20. if otwórz nawias okrągły wiersz otwórz nawias ostrokątny 8 zamknij nawias okrągły otwórz nawias klamrowy. Linia 21. hetman otwórz nawias okrągły wiersz plus 1 zamknij nawias okrągły średnik. Linia 22. zamknij nawias klamrowy else otwórz nawias klamrowy. Linia 23. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny znak równości 8 średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 24. cout otwórz nawias ostrokątny otwórz nawias ostrokątny h otwórz nawias kwadratowy i zamknij nawias kwadratowy średnik. Linia 25. zamknij nawias klamrowy. Linia 27. cout otwórz nawias ostrokątny otwórz nawias ostrokątny endl średnik. Linia 28. zamknij nawias klamrowy. Linia 29. zamknij nawias klamrowy. Linia 30. zamknij nawias klamrowy. Linia 31. zamknij nawias klamrowy. Linia 32. zamknij nawias klamrowy.

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 nawrotamialgorytm z nawrotamialgorytmem z nawrotami.

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int h otwórz nawias kwadratowy 9 zamknij nawias kwadratowy średnik. Linia 6. void hetman otwórz nawias okrągły int wiersz zamknij nawias okrągły otwórz nawias klamrowy. Linia 7. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny znak równości 8 średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 8. if otwórz nawias okrągły h otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości znak równości 0 zamknij nawias okrągły otwórz nawias klamrowy. Linia 9. int czyAtakowane znak równości 0 średnik. Linia 11. for otwórz nawias okrągły int j znak równości 1 średnik j otwórz nawias ostrokątny znak równości 8 średnik j plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 12. if otwórz nawias okrągły h otwórz nawias kwadratowy j zamknij nawias kwadratowy ampersant ampersant otwórz nawias okrągły abs otwórz nawias okrągły i minus j zamknij nawias okrągły znak równości znak równości abs otwórz nawias okrągły wiersz minus h otwórz nawias kwadratowy j zamknij nawias kwadratowy zamknij nawias okrągły zamknij nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 13. czyAtakowane znak równości 1 średnik. Linia 14. zamknij nawias klamrowy. Linia 15. zamknij nawias klamrowy. Linia 17. if otwórz nawias okrągły wykrzyknik czyAtakowane zamknij nawias okrągły otwórz nawias klamrowy. Linia 18. h otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości wiersz średnik. Linia 20. if otwórz nawias okrągły wiersz otwórz nawias ostrokątny 8 zamknij nawias okrągły otwórz nawias klamrowy. Linia 21. hetman otwórz nawias okrągły wiersz plus 1 zamknij nawias okrągły średnik. Linia 22. zamknij nawias klamrowy else otwórz nawias klamrowy. Linia 23. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny znak równości 8 średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 24. cout otwórz nawias ostrokątny otwórz nawias ostrokątny h otwórz nawias kwadratowy i zamknij nawias kwadratowy średnik. Linia 25. zamknij nawias klamrowy. Linia 27. cout otwórz nawias ostrokątny otwórz nawias ostrokątny endl średnik. Linia 28. zamknij nawias klamrowy. Linia 30. h otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości 0 średnik. Linia 31. zamknij nawias klamrowy. Linia 32. zamknij nawias klamrowy. Linia 33. zamknij nawias klamrowy. Linia 34. zamknij nawias klamrowy.

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++:

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int h otwórz nawias kwadratowy 9 zamknij nawias kwadratowy średnik. Linia 6. void hetman otwórz nawias okrągły int wiersz zamknij nawias okrągły otwórz nawias klamrowy. Linia 7. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny znak równości 8 średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 8. if otwórz nawias okrągły h otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości znak równości 0 zamknij nawias okrągły otwórz nawias klamrowy. Linia 9. int czyAtakowane znak równości 0 średnik. Linia 11. for otwórz nawias okrągły int j znak równości 1 średnik j otwórz nawias ostrokątny znak równości 8 średnik j plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 12. if otwórz nawias okrągły h otwórz nawias kwadratowy j zamknij nawias kwadratowy ampersant ampersant otwórz nawias okrągły abs otwórz nawias okrągły i minus j zamknij nawias okrągły znak równości znak równości abs otwórz nawias okrągły wiersz minus h otwórz nawias kwadratowy j zamknij nawias kwadratowy zamknij nawias okrągły zamknij nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 13. czyAtakowane znak równości 1 średnik. Linia 14. zamknij nawias klamrowy. Linia 15. zamknij nawias klamrowy. Linia 17. if otwórz nawias okrągły wykrzyknik czyAtakowane zamknij nawias okrągły otwórz nawias klamrowy. Linia 18. h otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości wiersz średnik. Linia 20. if otwórz nawias okrągły wiersz otwórz nawias ostrokątny 8 zamknij nawias okrągły otwórz nawias klamrowy. Linia 21. hetman otwórz nawias okrągły wiersz plus 1 zamknij nawias okrągły średnik. Linia 22. zamknij nawias klamrowy else otwórz nawias klamrowy. Linia 23. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny znak równości 8 średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 24. cout otwórz nawias ostrokątny otwórz nawias ostrokątny h otwórz nawias kwadratowy i zamknij nawias kwadratowy średnik. Linia 25. zamknij nawias klamrowy. Linia 27. cout otwórz nawias ostrokątny otwórz nawias ostrokątny endl średnik. Linia 28. zamknij nawias klamrowy. Linia 30. h otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości 0 średnik. Linia 31. zamknij nawias klamrowy. Linia 32. zamknij nawias klamrowy. Linia 33. zamknij nawias klamrowy. Linia 34. zamknij nawias klamrowy. Linia 36. int main otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 37. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny znak równości 8 średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 38. h otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości 0 średnik. Linia 39. zamknij nawias klamrowy. Linia 41. hetman otwórz nawias okrągły 1 zamknij nawias okrągły średnik. Linia 42. return 0 średnik. Linia 43. zamknij nawias klamrowy.

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

Praca domowa

Korzystając z zaprezentowanego programu, dokonaj jego uogólnienia i napisz program, który rozwiąże problem dla n hetmanów.

Słownik

warunek konieczny
warunek konieczny

warunek (zazwyczaj jeden z wielu), którego spełnienie jest konieczne dla zaistnienia danego faktu

warunek wystarczający
warunek wystarczający

warunek, którego spełnienie wystarcza dla zaistnienia danego faktu

rekurencja
rekurencja

technika programowania, w której funkcja odwołuje się do samej siebie

algorytm z nawrotami
algorytm z nawrotami

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