Labirynt, przejście od lewej do prawej

0

Mam zadanie, zrobić program, który sprawdza czy labirynt da się przejść z lewej do prawej. Do programu wpisuje się 8 cyfr, które są zamieniane na system 0-1. 0 to ściana a 1 to droga:

37 00100101
136 10001000
196 -> 11000100
96 01100000
127 01111111 <- ten labirynt da się przejść.
37 00100101
42 00101010
42 00101010

Mam już taki generator, ale nie mam pomysłu jak sprawdzi, czy labirynt da się przejść. Jeżeli ktoś może mi pomóc, to bardzo bym prosił, i z góry dzięki za odpowiedzi.

0

jeśli tylko masz sprawdzić czy da się przejść to możesz zrobić tak, że zawsze skręcasz w lewo a jak trafisz na koniec tunelu to zawracasz. Jak dojdziesz do prawej strony to jest przejście a jak do wejścia to nie ma

0

A jak mam to zrobić, If , pętla, czy może rekurencja ? Ja nie mam pojęcia jak sią za to zabrać ;/.

0

Ten sposób działa na labirynty w których nie ma przejść cyklicznych. W przeciwnym razie możesz zacząć się kręcić wokoło. Wyobraź sobie mieszkanie z drzwiami z pokoju A do B, z B do C i z C do A: mimo trzymania się cały czas jednej strony, nigdy nie trafiasz na balkon…

0

No właśnie mi się zdaje, że coś z If trzeba kombinować, i z każdej 1 po lewej zobaczyć jak daleko można zajść, aż się dojdzie do prawej. Nie wiem tylko jak sprawdzać kolejną cyfrę w rzędzie i przechodzić do linijki wyżej i niżej.

0

algorytm flood fill, chociaż nie wiem, czy nie jest nadmiernie skomplikowany w stosunku do tego typu zadań. linki do przykładów znajdziesz na google (i o dziwo na youtube) - labyrinth flood fill.
algorytm proponowany przez Miśkad też sobie poradzi, bo w takim labiryncie nie ma przejść cyklicznych, musisz tylko sprawdzić, czy nie wróciłeś do punktu startu (jeśli tak się stanie,to znaczy, że balkonu jednak nie ma ;)).

"if, pętla czy rekurencja" - wszystkie trzy (chociaż da się i bez rekurencji). jeśli nie umiesz wyobrazić sobie działania algorytmu i zaimplementować go, to nie startuj do takich zadań.

0

Nie startował bym, ale to praca semestralna z informatyki. Labirynt musi być podany w formie cyfr i program ma zamienić cyfry na system binarny i utworzyć z tego labirynt. To wszystko już mam ale nie wiem jak sprawdzić tą drogę, w labiryncie składającego się z poszczególnych pól (jak kratka w zeszycie z zamalowaną częścią kratek). Myślałem nad if (po lewej stronie) 1 then i szuka dookola tej jedynki innych. jak znajdzie to tak aż trafi na prawą stronę labiryntu.

0

Niewielka modyfikacja rekurencyjnego algorytmu do przeszukiwania grafu (wgłąb albo wszerz) załatwi sprawę.

1

Ja proponuję zrobić tak:
z tych stringów zrobić tablicę dwuwymiarową booleanów. jeśli jest ścieżka w punkcie [i,j] to wartość true, jeśli nie, to false.
dodatkowo zadeklarować sobie tablicę [1..64] rekordów [x,y] (rekord zawiera 2 integery które są współrzędnymi jakiegoś punktu)

37 00100101
136 10001000
196 -> 11000100
96 01100000
127 01111111 <- ten labirynt da się przejść.
37 00100101
42 00101010
42 00101010

np, dla tego labiryntu wartość [1,1] będzie false, [1,8] false, [3,1] true, itd (pierwsza współrzędna rośnie poziomo w prawo, druga pionowo w dół - 0,0 to górny lewy róg).

skonstruowanie takiej tablicy nie jest trudne - wystarczy utworzyć stringi z danych liczb w systemie (wystarczy włączyć bibliotekę STRUTILS, i można zamienić dowolną liczbę w sys. dziesiętnym na stringa zerojedynkowego, i nawet dodać ile się chce zer wiodących)

np.

uses strutils;
var s: string; i: integer;
begin
read(i);
s:=binStr(i, 8);
end.

string 's' będzie zawierał zera i jedynki zapisane w 8 charach, z zerami wiodącymi.

potem wystarczy skanować ten string, i ustawiać w tablicy odpowiednie wartości - true/false.

i potem:
skanujesz sobie tę utworzoną tablicę. jeśli w jakimś miejscu [1, i] jest wartość true, to znaczy że można tutaj zacząć zwiedzanie labiryntu. zaczynasz więc zwiedzanie. jeśli z danego punktu prowadzi tylko jedna droga do innego punktu, to wymazujesz z tablicy aktualną pozycję (na aktualnej pozycji ustawiasz FALSE, zapewnia to potem stabilność programu i zapobiega zapętleniom przy powrotach do rozwidleń). sprawdzenie możliwych dróg można bardzo łatwo wykonać:
jeśli jesteś w punkcie [i,j] to wystarczy sprawdzić:

  • [i-1,j]
  • [i+1, j]
  • [i, j-1]
  • [i, j+1]
  • jeśli w sumie na tych pozycjach jest więcej niż jedno true, to znaczy że z pozycji [i,j] prowadzi więcej niż jedna droga. analogicznie, jeśli nie ma żadnego true, znaczy że doszedłeś do ślepego zaułka. gdy dojdziesz do rozwidlenia, używasz tablicy rekordów o której wspominałem wcześniej. najlepiej trzymać zmienną dotyczącą tych rekordów, która będzie wskazywała na jakiej pozycji skończyłeś dopisywanie rekordów. np. jeśli dopisałeś 5 rekordów, to zmienna wskazująca na kolejne dopisanie=6. i po prostu gdy trzeba, odwoływać się do kolejnego rekordu (trzeba pamiętać o zwiększaniu 'wskazywacza' po każdym odwołaniu się do rekordu).
    i teraz tak:
  • jeśli jest jedna droga, to idziesz nią, i wymazujesz swój ślad.
  • jeśli napotkasz się na rozwidlenie, dodajesz punkt w którym jest rozwidlenie, na pozycji w tablicy rekordów, którą wskazuje ci wskazywacz zapisywania. idziesz dowolnie wybraną drogą, i robisz analogicznie - gdy jest to jedna droga, wymazujesz, jeśli nie, zapisujesz współrzędne rozwidlenia. gdy dojdziesz do jakiegoś indeksu [8, i] to znaczy że wyszedłeś z labiryntu. jeśli dojdziesz do ślepego zaułka, to:
    a) jeśli masz jakiś rekord wskazujący na rozwidlenie, sprawdzasz, w których kierunkach możesz z niego iść, i idziesz. jeśli nie ma żadnego kierunku w którym możesz iść, to wracasz do poprzedniego rozwidlenia, i tam sprawdzasz czy możesz iść, itd, aż nie będziesz miał żadnych rekordów wskazujących na rozwidlenia w tym korytarzu. w takim wypadku, skanujesz kolejne linie, aż natkniesz się na kolejne wejście do labiryntu które ma indeks [1, i], i tam wykonujesz to samo błądzenie.
    b) jeśli nie ma już żadnych rekordów wskazujących na rozwidlenia, i żadnych innych wejść do labiryntu, odpowiedź brzmi nie.
    c) jeśli doszedłeś do jakiegoś indeksu [8,i] (w sumie nawet nie trzeba do niego dochodzić - wystarczy, że dotrzemy do indeksu [7,i] i sprawdzimy czy w miejscu [8,i] jest TRUE - jeśli jest, to jest to wyjście z labiryntu) to odpowiedź brzmi TAK

jeśli masz jakieś pytania, to wal :P

przykładowy przebieg algorytmu dla tego labiryntu:

37 00100101
136 10001000
196 -> 11000100
96 01100000
127 01111111 <- ten labirynt da się przejść.
37 00100101
42 00101010
42 00101010

skanujesz tablicę. pierwszym wejściem jest [1,2] z którego prowadzi tylko jedna droga do [1,3]. w miejscu 1,2 ustawiasz FALSE aby nie próbować drugi raz wejść tym wejściem i się nie zapętlić. jesteś w 1,3, i robisz tak jak poprzednio. idziesz do 2,3 jedyną drogą, potem do 2,4 gdzie jest rozwidlenie (bo jest więcej niż jedna sąsiadująca komórka która ma TRUE). dodajesz współrzędne rozwidlenia do tablicy na pierwszej pozycji, jednocześnie w głównej tablicy ustawiając wartość FALSE w miejscu rozwidlenia. zwiększasz pozycję kolejnego zapisania na 2. idziesz dowolną drogą, przypuśćmy, że do 3,4. jedyna droga jest do 3,5, gdzie jest rozwidlenie. ustawiasz tam false, i dodajesz na pozycji drugiej współrzędne rozwidlenia. tym razem wybierasz drogę w dół, osiągasz punkt [3,8] który jest ślepym zaułkiem (po drodze trzeba pamiętać o wymazywaniu odwiedzonych ścieżek przez ustawianie FALSE). tak więc wracasz do ostatnio dodanego rekordu, którym jest nr 2. (aktualna pozycja zapisywania jest na 3, więc ostatnio dodany rekord jest na miejscu 3-1). tak więc wracasz do punktu 3,5 z którego prowadzi teraz dwie drogi - na prawo i na lewo. idziesz na lewo do 2,5. masz tylko jedną drogę do 2,4, gdzie jest ślepy zaułek (zauważ, że podczas wcześniejszego wykonywania algorytmu, wymazaliśmy ścieżki w miejscu 2,3 i 3,4, więc pozycję 2,4 program odczyta jako ślepy zaułek. wracasz w takim razie do 3,5. prowadzi z niego już tylko jedna droga, więc można wymazać ten rekord z tablicy, i ustawić pozycję zapisywania na 2 (na pozycji pierwszej mamy punkt 2,4 - gdybyśmy się do niej odwołali, to byłoby 0 możliwych ścieżek, i wymazalibyśmy go również - ale program tego nie wie, więc zostawia sobie ten rekord zapamiętany). idziemy więc z rozwidlenia 3,5 jedyną możliwą ścieżką, zamazując. dochodząc do 6,5 mamy rozwidlenie. zamazujemy go, dodajemy współrzędne tego rozwidlenia na poz. 2. idziemy w dół, gdzie jest ślepy zaułek. wracamy się więc do ostatnio dodanego rozwidlenia 6,5. i tym razem idziemy jedyną możliwą ścieżką. dochodzimy do 7,5. w tym miejscu, sprawdzając możliwe ścieżki, dowiadujemy się, że na pozycji [8,5] jest true, co jest równoznaczne z wyjściem z labiryntu. odpowiedź pozytywna.

0

Ja bym jednak zrobił tak jak Mariusz BFS-a (z kolejką) - do kolejki wrzucasz na początku kilka punktów startowych, robisz BFS-a, jak natrafisz na punkt wyjścia zwracasz true, inaczej false.

0

przykładowe funkcje i procedury do programu:

function IleMozliwych(i,j: integer):integer; //przekazujemy wspolrzedne punktu w ktorym jestesmy aby sprawdzic na ile sposobow mozemy z niego wyjsc
begin
IleMozliwych:=0;
if labirynt[i-1,j]=TRUE then inc(ilemozliwych);
if labirynt[i+1,j]=TRUE then inc(ilemozliwych);
if labirynt[i,j+1]=TRUE then inc(ilemozliwych);
if labirynt[i,j-1]=true then inc(ilemozliwych);
end;

fragment odpowiedzialny za dodawanie rozwidleń

//jesteśmy  punkcie i,j. pozycja zapisywania =3 (tzn, dodaliśmy już wcześniej 2 rozwidlenia)
if IleMozliwych(i,j) > 1 then 
begin
      with rozwidlenia[pozycja_zapisywania] do 
      begin
         x:=i; y:=j; 
      end;
      inc(pozycja_zapisywania);
end;
labirynt[i,j]:=false

edit: trzeba jeszcze zadeklarować zmienną, (rekord x,y) który będzie wskazywał na aktualną pozycję w labiryncie, i odpowiednio ją zmieniać przy zwiedzaniu. możemy wejść tylko na pole TRUE, któremu ustawiamy FALSE po wejściu na nie. gdy dotrzemy do miejsca gdzie IleMozliwych=0 to ustawiamy aktualna pozycje na ostatnie rozwidlenie i tam działamy od nowa.

0

Dziękuję za tyle odpowiedzi. Zaraz zaczynam coś kombinować tylko muszę jeszcze zrozumieć działanie tablicy 2 wymiarowej.

0

nie mam pod ręką kompilatora delphi, więc napisałem w c# implementację przejścia przez labirynt z wykorzystaniem algorytmu flood fill:
static bool floodFillSolver(string[] l)
{
int h = l.Length, w = l[0].Length;

// przepisanie tablicy 1D stringów na tablicę 2D charów (w c# nie można bezpośrednio modyfikować znaków w stringu)
var f = new char[h, w];
for (int i = 0; i < h; i++)
	for (int j = 0; j < w; j++)
		f[i, j] = l[i][j];

// pętla po wszystkich elementach pierwszej kolumny - "miejsca startu", czyli jedynki w pierwszej kolumnie
for (int i = 0; i < h; i++)
{
	if (f[i, 0] == '1')
	{
		f[i, 0] = '2';

		bool changed = false;
		
		// flood fill:
		// ilość przebiegów - ekstremalny przypadek w/2*h/2, na wypadek, gdyby 
		// ścieżka wielokrotnie zasuwała pod górkę albo w lewą stronę; ze względu
		// na optymalizację kończącą pętlę po wykryciu pustego przebiegu równie 
		// dobrze może to być pętla nieskończona
		for (int n = 0; n * 4 < w * h; n++)
		{
			// jeśli w przebiegu nie został zmieniony żaden element, 
			// to stan mamy ustalony i koniec flood filla
			for (int x = 0; x < h; x++)
				for (int y = 0; y < w; y++)
				{
					if (f[y, x] == '2')
					{
						if (x == w - 1) // dwójka w ostatniej kolumnie - flood fill dotarł do prawego brzegu
							return true;

						// sprawdzenie, czy w sąsiedztwie dwójki znajdują się 1, jeśli tak, to zmieniane w 2
						if (y > 0     && f[y - 1, x] == '1') { f[y - 1, x] = '2'; changed = true; }
						if (x > 0     && f[y, x - 1] == '1') { f[y, x - 1] = '2'; changed = true; }
						if (y < h - 1 && f[y + 1, x] == '1') { f[y + 1, x] = '2'; changed = true; }
						if (x < w - 1 && f[y, x + 1] == '1') { f[y, x + 1] = '2'; changed = true; }

					}
				}

			// optymalizacja - zlikwidowanie pustych przebiegów (jeśli 
			// w tej iteracji flood fill nic się nie zmieniło, to w następnej
			// też się nie zmieni); break zamiast return false ze wzgledu na
			// możliwość wielu punktów startowych (patrz pierwsza pętla)
			if (!changed) break;
		}
	}
}

return false;

}


wywołanie:
<code class="c#">Console.WriteLine(floodFillSolver(new string[] {
	"00100101",
	"11111011",
	"11001010",
	"10001010",
	"10111010",
	"10100110",
	"10101010",
	"10111110"
}));

oczywiście całość można przerobić na operowanie na tablicy 1d bajtów/intów/czegokolwiek, ale mi się nie chciało, a w c# najłatwiej było 2d char.

0

A co polecacie na labirynt z cyklami?

1 użytkowników online, w tym zalogowanych: 0, gości: 1