<b>function</b> Labirynt(M,N:integer; <b>var</b> A:<b>array</b>[1..M,1..N] <b>of</b> integer; i1,j1,i2,j2:integer):boolean;
// M,N >= 1
// A zawiera 0 (ściana) i 1 (droga)
<b>function</b> szukaj(i,j:integer):boolean;
// 1 <= i <= M
// 1 <= j <= N
<b>var</b> jest:boolean;
<b>begin</b>
<b>if</b> (i=i2) <b>and</b> (j=j2) <b>then</b>
jest:=true
<b>else</b> <b>begin</b>
jest:=false;
<b>if</b> A[i,j]>0 <b>then</b> <b>begin</b>
A[i,j]:=-1; <b>if</b> i>1 <b>then</b> jest:=szukaj(i-1,j);
<b>if</b> <b>not</b> jest <b>and</b> (i<M) <b>then</b> jest:=szukaj(i+1,j);
<b>if</b> <b>not</b> jest <b>and</b> (j>1) <b>then</b> jest:=szukaj(i,j-1);
<b>if</b> <b>not</b> jest <b>and</b> (j<N) <b>then</b> jest:=szukaj(i,j+1);
<b>end</b>
<b>end</b>;
szukaj:=jest
<b>end</b>; // szukaj
<b>var</b> i,j:integer;
<b>begin</b> // Labirynt
<b>if</b> <b>not</b>((1<=i1) <b>and</b> (i1<=M) <b>and</b> (1<=j1) <b>and</b>
(j1<=N) <b>and</b> (1<=i2) <b>and</b> (i2<=M) <b>and</b> (1<=j2) <b>and</b> (j2<=N)) <b>then</b>
Labirynt:=false
<b>else</b>
<b>begin</b>
Labirynt:=szukaj(i1,j1);
<b>for</b> i:=1 <b>to</b> M <b>do</b>
<b>for</b> j:=1 <b>to</b> N <b>do</b>
<b>if</b> A[i,j]=-1 <b>then</b> A[i,j]:=1
<b>end</b>
<b>end</b>; // Labirynt
Koszt czasowy: liniowy względem rozmiaru A (czyli M×N)
Koszt pamięciowy: liniowy względem rozmiaru A
Opis: Przeszukujemy wgłąb tablicę zaznaczając odwiedzone pola poprzez ustawienie ich wartości na -1. Dzięki temu nie zapętlimy się ani nie będziemy przetwarzać wielokrotnie tych samych pól.
Dyskusja: Funkcja 'Labirynt' jest otoczką, która sprawdza poprawność parametrów, wywołuje właściwą funkcję rekurencyjną 'szukaj' oraz sprząta po niej zaznaczenia w tablicy A.
Wewnętrzna funkcja rekurencyjna 'szukaj' ma za zadanie znaleźć ścieżkę z punktu (i,j) do punktu (i2,j2) po niezaznaczonych polach w tablicy A. Korzysta ona z kilku nazw zadeklarowanych przez otoczkę (M, N, A, i2, j2), dlatego nie są one parametrami wywołania funkcji 'szukaj'.
Funkcja 'szukaj':
- sprawdza czy punkt (i,j) nie jest poszukiwanym końcem (w tym przypadku zwraca true),
- sprawdza czy punkt (i,j) nie jest ścianą lub punktem odwiedzonym wcześniej (w tym przypadku zwraca false),
- zaznacza punkt (i,j) jako odwiedzony
- próbuje szukać drogi z punktów sąsiednich dla punktu (i,j), o ile sąsiedzi Ci istnieją.
Zauważ, że testy 1 i 2, mogłyby być wykonane przed każdym wywołaniem funkcji 'szukaj' w punkcie 4 (oraz w otoczce) zamiast na jej początku, jednak prowadziłoby to do wielokrotnego pisania bardzo podobnych fragmentów programu, co łatwo prowadzi do błędów. Oczywiście nie zmniejszyłoby to złożoności czasowej i pamięciowej.
Podobnie jak testy 1 i 2, testy istnienia sąsiadów (i>1), (i<M) itp. w punkcie 4 mogłyby być wykonane na początku funkcji 'szukaj' (wtedy nie zakładalibyśmy, że (i,j) jest poprawnym punktem w tablicy), ale nie mając wiedzy w którą stronę ostatnio poszliśmy, musielibyśmy sprawdzić pełną poprawność obu współrzędnych (i,j), czyli w sumie sprawdzalibyśmy 4 warunki. W aktualnej wersji sprawdzamy tylko 1 warunek.
Poprawność rozwiązania: Oczywiste jest, że jeśli funkcja 'Labirynt' da wynik true, to ścieżka z (i1,j1) do (i2,j2) istnieje. Mniej oczywiste jest, że jeśli funkcja da wynik false, to ścieżka na pewno nie istnieje.
Aby przeprowadzić dowód przez sprzeczność, załóżmy, że funkcja 'szukaj' wywołana w funkcji 'Labirynt' dała wynik false, a ścieżka z (i1,j1) do (i2,j2) istnieje. W takim razie A[i1,j1]=-1 a A[i2,j2]=1. Wynika z tego, że ścieżka z (i1,j1) do (i2,j2) w którymś miejscu opuszcza zaznaczoną część tablicy, czyli istnieją dwa sąsiednie pola (i,j) i (i',j') na tej ścieżce, takie że A[i,j]=-1, A[i',j']=1. Z tego wynika, że funkcja szukaj została (w czasie działania programu) wywołana z parametrami (i,j), ale nie została wywołana z parametrami (i',j'). Jest to niemożliwe, bo pola te sąsiadują ze sobą i wywołanie dla (i,j) wywołałoby rekurencyjnie 'szukaj' dla (i',j').