Добавил:
ПОИТ 2016-2020 Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
40
Добавлен:
29.04.2018
Размер:
824.83 Кб
Скачать

Int test(char *s, char *r)

{ int n; n = strlen(s);

if (n == 0) return 1;

for (; *s != 0 && n > 1; s++, n--)

if (strncmp(s, r, n) == 0) return 1; //Сравнивает не более n символов строк s и r. Если s < r, то //рез. <0, если s = r, то рез. = 0, если s > r, то рез.>0.

return 0;

}

int Stepp(char *lw) // параметр – тек. слово цепочки

{ setlocale (LC_CTYPE, "Russian");

int n;

for (n = 0; w[n] != NULL; n++)

if (*w[n] != 0) break;

if (w[n] == NULL) // цепочка выстроена return 1;

for (n = 0; w[n] != NULL; n++)

{ char *pw; // проверка на присоед. очеред. слова

if (*w[n] == 0) continue; // пустое слово - пропустить

pw = w[n]; w[n] = ""; // исключить проверяемое слово из множества

if (TEST(lw, pw)) // попытка присоединить слово

{ if (Stepp(pw)) //присоединено-попытка вывести

{ cout << pw << "\n"; //цепочку из нового слова

return 1; } // удача - вывести слово и выйти

}

w[n]=pw; // возвратить исключенное слово

}

return 0;

}

Void main() { Stepp(""); }

Функция TEST проверяет, не совпадает ли окончание первой строки с началом второй путем обычного сравнения строк при зад. длине “хвоста” первой строки. 

Функция TEST при первом параметре - пустой строке возвращает ИСТИНУ при любом виде второй строки. Этого достаточно, чтобы запустить первый шаг рекурсии. При наличии пустой строки в кач. параметра функции step на первом шаге рекурсии б. производиться проверка каждого слова на предмет создания цепочки. 

Данная прогр. не содер. некот. частностей, кот. не меняют общей картины:

- нет проверки условия завершения рекурсии , т.е. если массив указателей содержит только пустые строки, то рекурс. последов.сть шагов завершена успешно (все слова выбраны на предыдущих шагах);

- нет проверки совпадения “хвоста” очередного слова и начала выбираемого на тек. шаге - делается отдельной функцией;

- сама цепочка выбранных слов выводится в процессе “ретрансляции” положительного результата непосредственно на экран в обратном порядке (что не совсем “красиво”). В принципе она можноб. сформирована и в глобальных данных.   

Рекурсия и поисковые задачи

С помощью рекурсии можно решать задачи, связанные с поиском и основанные на полном или частичном переборе возможных вариантов.

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

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

При реализации поисковых задач следует обратить внимание на результат рекурсивной функции:

- если рекурсивная функция имеет результат void, то алгоритм будет выполнять полный перебор всех возможных вариантов;

- если рекурсивная функция выполняет поиск первого попавшегося варианта, то результатом ее является, как правило, логическое значение. При этом “ИСТИНА” соответствует успешному завершению поиска, а “ЛОЖЬ” - неудачному.

(Общее для всех алгоритмов поиска: если рекурсивная функция возвращает “ИСТИНУ”, то она должна быть “передана наверх”, то есть текущий вызов должен быть завершен со значением “ИСТИНА”.

Если рекурсивный вызов возвращает “ЛОЖЬ”, то поиск должен быть продолжен.

При завершении полного перебора всех вариантов с неудачным результатом рекурсивная функция должна возвратить “ЛОЖЬ”)

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

Пример. Поиск выхода в лабиринте

Задан лабиринт в виде двумерного массива, в котором 1 обозначает "стенку", а 0 - "проход".           #define NX 5               // количество точек по оси X       #define NY 5                // количество точек по оси Y          int nx = NX - 1, ny = NY - 1;        // последние значения координат X и Y          int Lbr[NX][NY] = { {1,1,0,1,1},

{1,1,0,1,1},

{1,1,0,0,1},

{1,1,1,0,0},

{1,0,1,1,1} };

Шаг алгоритма состоит в проверке возможности сделать ход в одном из четырех направлений. 

Рекурсивный характер алгоритма состоит в том, что в каждой соседней точке реализуется тот же самый алгоритм поиска. 

Формальными параметрами рекурсивной функции в данном случае являются координаты точки, из которой в данный момент осуществляется поиск. 

Фактические параметры - координаты соседней точки, в которой реализуется рекурсивный алгоритм 

          void step(int x, int y) // формальные параметры - координаты точки, откуда идет поиск           {          …                       step(x + 1, y); ...                       step(x, y + 1); ...                       step(x - 1, y); ...                       step(x, y - 1); ...                       …            }

 Результат рекурсивной функции логический - он показывает, можно ли через данную точку достигнуть выхода.

Если в текущем шаге рекурсии через некоторую соседнюю точку можно достигнуть выхода, то рекурсивный вызов возвратит результат ИСТИНА, который должен быть передан и в предыдущую точку (то есть в предыдущий вызов).

Если ни одна соседняя точка не возвращает положительного результата, то результат текущего шага также отрицателен.           int step(int x, int y)           {                      ...                       if (step(x+1, y)) { … return 1; }                       if (step(x, y+1)) { … return 1; }                       if (step(x-1, y))  { … return 1; }                       if (step(x, y-1))  { … return 1; }                       ...                       return 0;           }

Надо добавить возвращение найденного пути. Можно использовать глобальные данные Lbr для отметки "хороших" точек:           int step(int x, int y)           {                      ...                       if (step(x+1, y)) { Lbr[x][y]=2; return 1;}                       if (step(x, y+1)) { Lbr[x][y]=2; return 1;}                       if (step(x-1, y)) { Lbr[x][y]=2; return 1;}                       if (step(x, y-1)) { Lbr[x][y]=2; return 1;}                       ...                       return 0;           }

Следующий шаг - отсечение недопустимых точек, в данном случае это - стенки лабиринта. Здесь же вводится ограничение рекурсии - выход найден, если достигнут край лабиринта:          

int step(int x, int y) {  if (Lbr[x][y] == 1) return 0;              // стенки лабиринта   if (x == 0 || x == nx  || y == 0 || y == ny) 

{ … return 1; }     // края лабиринта - выход                       …                       if (step(x+1, y)) { Lbr[x][y]=2; return 1;}                       if (step(x, y+1)) { Lbr[x][y]=2; return 1;}                       if (step(x-1, y)) { Lbr[x][y]=2; return 1;}                       if (step(x, y-1)) { Lbr[x][y]=2; return 1;}                       ...                       return 0;           }

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

#include <iostream>

using namespace std;

#define NX 5 // количество точек по оси X

#define NY 5 // количество точек по оси Y

int nx = NX - 1, ny = NY - 1; // последние значения корд. X и Y

int Lbr[NX][NY] = { {1,1,0,1,1},

{1,1,0,1,1},

{1,1,0,0,1},

{1,1,1,0,0},

{1,0,1,1,1} };

Соседние файлы в папке Лекции