|
Z Wikipedii
Labirynt (gr.labyrinthos) - ciąg korytarzy, w którym trudno znależć wyjście. Używany czasami w matematyce i w logice. Słowo labirynt jest właściwie niegreckiego pochodzenia, często uważa się, że pochodzi od przedgreckiego (pelazgijskiego) słowa labrys, które oznaczało obusieczny topór.
|
Metod szukania wyjścia/wyjść z labiryntu jest chyba trzy ( nie licząc ich modyfikacji )
np taka:
DOPÓKI (nie trafiłeś na wyjście LUB nie trafiłeś do punktu startowego)
na każdym skrzyzowaniu zawsze skręcasz w tę samą stronę (np w prawo).
|
Nasz aplet proponuje szukanie wyjścia z plataniny korytarzy (czasami) i większych wolnych obszarów (jaskiń), nie jest przeto "klasycznym labiryntem".
Mimo wszystko dalej będę go nazywał labiryntem ( ze zwykłej wygody ).
Obserwując poprzedni aplet "wypełnij obszar", nasuwa sie nieodparte wrażenie, że w trakcie wypełniania, widoczne jest niezwykłe dążenie, aby jak najszybciej trafić do brzegu figury. Wykorzystamy to.
Rozwiążę zadanie:
Startując z wewnętrznego punktu "labiryntu" znaleźć wszystkie możliwe wyjścia (tym samym drogi do tych wyjść) |
Tym razem zastosuję rozwiązanie rekurencyjne. Wolne pola "labiryntu" nie są w przesadnie wielkiej liczbie i raczej nie grozi nam kłopot : Stack overflow. Gdyby jednak, znamy rozwiązanie alternatywne (patrz wypełnianie).
Przy tym podejściu nie ma potrzeby korzystania z liczb losowych oraz struktur typu lista / stos.
W aplecie zastosowalem jednak stos, by zilustrować proces szukania i pokazać nieduże koszty algorytmu w prezentowanej sytuacji.
void losuj_lab( )
{
for(int i=1;i<w-1;i++)
for(int j=1;j<k-1;j++)
{
lab[j][i]=mat.losowa_c(5)<3;
// zapelniam labirynt bez brzegów
}
for(int i=0;i<k;i++)
{
lab[i][0]=(mat.losowa_c(5)==0);
lab[i][w-1]=(mat.losowa_c(5)==0);
// wypelniam brzegi lewy i prawy
}
for(int i=0;i<w;i++)
{
lab[0][i]=(mat.losowa_c(5)==0);
lab[k-1][i]=(mat.losowa_c(5)==0);
// wypelniam brzeg górny i dolny
}
lab[k/2-1][w/2-1]=true;
// zapewniam dostepnym srodek labiryntu
}
|
Teraz zbuduję "labirynt" o wymiarach w x k (w przykładzie w=k, ale to bez znaczenia), jego stan zapamiętam w tablicy boolean lab[k][w], gdzie pole "wolne" oznaczę true zaś element muru false. Losowo wypełnię elementy tablicy lab z prawdopodobieństwem p→true, i z prawdopodobienstwem 1-p→false. Odrębne losowanie zapewni "wyjścia" na brzegach.
Algorytm wypisujacy adresy wyjść można ująć w formie funkcji logicznej szukaj (zapewniamy sobe skończenie watku rekurencyjnego !!!)
boolean szukaj(int k_,int w_)
{boolean czy=true;
if(brzeg(k_,w_))
{
wypisz(k_,w_);
return true;
}
lab[k_-1][w_-1]=false;
if(lab[k_+1][w_])czy=szukaj(k_+1,w_);
if(lab[k_-1][w_])czy=szukaj(k_-1,w_);
if(lab[k_][w_+1])czy=szukaj(k_,w_+1);
if(lab[k_][w_-1])czy=szukaj(k_,w_-1);
return czy;
}
|
Zdefiniuję funkcje sprawdzającą, czy osiagnąłem już brzeg labiryntu:
boolean brzeg(int x,int y)
{
return(x==1 || x==k || y==1 || y==w);
}
|
Funkcja szukaj to tylko zapis idei algorytmu.
Spróbuj samodzielnie wykonać wizualizację. Nieskromnie sugeruję, by tak jak w widocznym aplecie pokazać caly proces szukania wyjścia, z prezentacją momentów wycofywania się z nietrafnych kierunków.
Uwaga:
Funkcja szukaj (w prezentowanej formie) zawsze zwraca wartość true.
|