Студопедия

Главная страница Случайная страница

КАТЕГОРИИ:

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Поиск в ширину. Волновой алгоритм






Поиск в ширину также встречался в задаче поиска путей на графе [9]. Есть множество других задач, для решения которых используется этот же подход. Если при поиске в глубину последовательно достраивается единственное частичное решение, то при поиске в ширину происходит переход от одного частичного решения к другому. Поиск решения производится широким фронтом. Типичным примером поиска в ширину является алгоритм Дейкстры поиска кратчайшего пути на графе.

Рассмотрим схему поиска в ширину на следующем примере.

Игра Lines. В таблице из N строк и N столбцов некоторые клетки заняты шариками, другие свободны. Выбран шарик, который нужно переместить, и место, куда его нужно переместить. Выбранный шарик за один шаг перемещается в соседнюю по горизонтали или вертикали свободную клетку. Требуется выяснить, возможно ли переместить шарик из начальной клетки в заданную, и, если возможно, то найти путь из наименьшего количества шагов.

Ввод из файла INPUT.TXT. В первой строке находится число N, в следующих N строках - по N символов. Символом точки обозначена свободная клетка, латинской заглавной O - шарик, @ - исходное положение шарика, который должен двигаться, латинской заглавной X - конечное положение шарика.

Вывод в файл OUTUT.TXT. В первой строке выводится Y, если движение возможно, или N, если нет. Если движение возможно, далее следует N строк по N символов - как и на вводе, но буква X, а также все точки по пути заменяются плюсами.

Ограничения: 2 ≤ N ≤ 40, время 1 с.

Примеры

Ввод 1 Ввод 2 Ввод 3

5 5 5

.... X.. X..... X.

. O O O O..........

..... O O O O O O. O O O

O O O O...........

@...... @...... @

Вывод 1 Вывод 2 Вывод 3

Y N Y

+ + + + +.. + +.

+ O O O O. + +..

+ + + + + O + O O O

O O O O +. + + + +

@ + + + +.... @

 

Для решения задачи используется так называемый волновой алгоритм. В целях удобства организуем барьер, объявив двумерный массив от 0 до N+1 по каждому измерению. Во все барьерные клетки занесем признаки занятости в виде символов ‘O’. Это избавит нас от необходимости проверять каждый раз, находимся ли мы на краю поля.

В начальную клетку поместим числовую метку 0. Далее во все соседние с ней пустые клетки занесем метку 1. Во все соседние клетки с меткой 1, помеченные символом ‘.’ как свободные, поместим метку 2 и т. д. Будем считать на каждом шаге количество вновь занесенных меток.

В конце концов, возможна одна из двух ситуаций:

на очередном шаге помечена конечная клетка;

новых меток не появилось.

В первом случае значение метки K равно длине кратчайшего пути из начальной клетки в конечную. Сам путь восстанавливается в обратном направлении. Для конечной клетки находится одна из соседних к ней, имеющая метку K-1. Потом от нее находится следующая клетка с меткой K-2 и так до тех пор, пока не придем в начальную клетку.

Второй случай говорит об отсутствии решения.

Проще всего на каждом шаге находить все клетки с определенным значением метки полным перебором. Такой подход не годится для больших размерностей. В программе Lines, приведенной ниже, клетки, получающие очередные метки, записываются в кольцевую очередь. Выбор клеток из начала очереди обеспечивает просмотр по возрастанию меток.

 

Program Lines;

Const

Max=150;

Raz=10*Max; {максимальный размер очереди}

Type

Coord=record

X, Y: byte; {координаты}

end;

Me=record

X, Y: byte; {координаты}

Metka: integer; {числовая метка}

end;

Var

i, j, k, Met, p, q: integer;

N, M: integer; {размер участка}

C: array[1..Max, 1..Max] of char;

From: array[0..Max+1, 0..Max+1] of char;

{откуда получена метка: U-сверху, D-снизу, L-слева, R-справа}

{используется также как рабочая карта поля с барьером из 'O'}

Fin, Fout: text;

Och: array[1..Raz] of Me; {кольцевая очередь для поиска в ширину}

BegO, EndO, Count: integer; {для кольцевой очереди}

Nach, Kon: Coord; {начальная и целевая клетки для поиска в ширину}

b: boolean; {признак нахождения решения}

Procedure Zanes(u, v: byte; Var Res: boolean); { занесение в конец кольцевой очереди }

{ (u, v)-текущая клетка, Res=true-дошли до конца }

Begin

if (u=Kon.X) and (v=Kon.Y) then

Res: =true {достижение цели}

else

begin

Res: =false;

EndO: =(EndO mod Raz)+1; {кольцевая очередь}

Och[EndO].X: =u;

Och[EndO].Y: =v;

Och[EndO].Metka: =Met+1; {текущая числовая метка}

Count: =Count+1;

end;

End;

Begin

Assign(Fin, 'input.txt');

Reset(Fin);

ReadLn(Fin, N);

For i: =0 to N+1 do

For j: =0 to N+1 do

From[i, j]: ='O'; {барьер}

For i: =1 to N do

begin

For j: =1 to N do

begin

Read(Fin, C[i, j]);

From[i, j]: =C[i, j]; { сначала копия C }

if From[i, j]='@' then {начало}

begin

Nach.X: =i;

Nach.Y: =j;

From[i, j]: ='O'; {запрет, чтобы не возвращаться}

end;

if From[i, j]='X' then {конец}

begin

Kon.X: =i;

Kon.Y: =j;

From[i, j]: ='.';

end;

end;

ReadLn(Fin); {перевод строки}

end;

Close(Fin);

BegO: =1; {кольцевая очередь для расстановки меток в ширину}

EndO: =1;

Count: =1; {длина очереди}

Och[BegO].Metka: =0;

Och[1].X: =Nach.X; Och[1].Y: =Nach.Y; {начальная точка}

b: =false;

While Count> 0 do

begin

p: =Och[BegO].X;

q: =Och[BegO].Y;

Met: =Och[BegO].Metka;

Och[BegO].X: =0; {для наглядности отладки}

Och[BegO].Y: =0;

BegO: =(BegO mod Raz)+1;

Count: =Count-1; {выбор клетки из начала очереди}

{далее занесение соседних клеток в конец очереди}

if From[p, q+1]='.' then {можно идти направо}

begin

Zanes(p, q+1, b); {занесение в очередь}

From[p, q+1]: ='L'; {пришли слева}

if b then Break; {достигли заданной клетки}

end;

if From[p+1, q]='.' then {можно идти вниз}

begin

Zanes(p+1, q, b); {занесение в очередь}

From[p+1, q]: ='U'; {пришли сверху}

if b then Break; {достигли заданной клетки}

end;

if From[p, q-1]='.' then {можно идти налево}

begin

Zanes(p, q-1, b);

From[p, q-1]: ='R'; {пришли справа}

if b then Break; {достигли заданной клетки}

end;

if From[p-1, q]='.' then {можно идти вверх}

begin

Zanes(p-1, q, b);

From[p-1, q]: ='D'; {пришли снизу}

if b then Break; {достигли заданной клетки}

end;

end; {здесь оказываемся либо при нахождении решения, либо по исчерпанию очереди, когда больше помечать нечего}

Assign(Fout, 'output.txt');

Rewrite(Fout);

if b then {найдено решение}

begin

WriteLn(Fout, 'Y');

p: =Kon.X; q: =Kon.Y; {восстановление от конца}

For i: =1 to Met+1 do {клетку '@' не меняем}

begin

C[p, q]: ='+';

Case From[p, q] of

'U': p: =p-1;

'D': p: =p+1;

'L': q: =q-1;

'R': q: =q+1;

end;

end;

For i: =1 to N do

begin

For j: =1 to N do Write(Fout, C[i, j]);

WriteLn(Fout); {перевод строки}

end;

end

else WriteLn(Fout, 'N');

Close(Fout);

End.

 


Поделиться с друзьями:

mylektsii.su - Мои Лекции - 2015-2024 год. (0.014 сек.)Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав Пожаловаться на материал