При закрытии подписчики были переданы в рассылку "Обзор инструментов SEO-оптимизатора и методов продвижения" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
| Информационный Канал Subscribe.Ru |
Олимпиадные задачи и алгоритмы на C++Здравствуйте, уважаемые подписчики.
Поиск в ширину на примере задачи о лабиринте.Постановка задачи. Лабиринт задан таблицей n*m из символов. Символ '#' - означает стену, '.' - пустое пространство, 's' - начальную позицию, 'f' - конечную позицию. Игрок может находиться только в пустых клетках. Из клетки он может перемещаться в соседнюю по горизонтали или по вертикали, но не по диагонали. Требуется найти кратчайший путь из начальной клетки в конечную. Пример лабиринта и кратчайшего пути в нем (n=4, m=6): ...... 23456. s#.#.. 1#.#7. .#.#.. .#.#8. ...#f. ...#9. Решение. Для решения используется алгоритм поиска в ширину. Собственно, поиском в ширину его обычно называют, когда говорят про графовые алгоритмы. В задачах о лабиринтах его чаще называют волновым алгоритмом, или просто волной.
// -------------------------------------------------------------------------
// lab - двухмерный массив, в котором хранится лабиринт
// n,m - количество строк и столбцов в массиве lab
// si,sj - начальная позиция
// fi,fj - конечная позиция
typedef struct point {int i, j;} point;
int di[4] = { -1, 0, 1, 0};
int dj[4] = { 0, 1, 0, -1};
int i,j;
int d[n][m]; // массив расстояний
int head,tail; // указатели очереди
point queue[n*m]; // очередь
// обнуляем массив расстояний
for (i = 0; i < n; i++)
for (j = 0; j < m; j++)
d[i][j] = 0;
d[si][sj] = 1; // расстояние до начальной клетки равно 1
// инициализируем очередь
head = tail = 0;
queue[tail].i = si;
queue[tail++].j = sj; // заносим в очередь начальную клетку
while (head < tail) // цикл пока очередь не пуста
{
point p = queue[head++]; // берем следующую позицию из очереди
for (int k = 0; k < 4; k++) // цикл по соседним клеткам
{
point newp;
newp.i = p.i + di[k];
newp.j = p.j + dj[k];
// проверяем, что такая клетка есть
if (0 <= newp.i && newp.i < n && 0 <= newp.j && newp.j < m)
// проверяем, что она свободна и ранее ее не посещали
if (lab[newp.i][newp.j] != '#' && d[newp.i][newp.j] == 0)
{
d[newp.i][newp.j] = d[p.i][p.j] + 1; // находим расстояние
queue[tail++] = newp; // заносим позицию в очередь
}
}
}
if (d[fi][fj])
printf("расстояние = %d\n", d[fi][fj]);
else
printf("нет ни одного пути");
// -------------------------------------------------------------------------
Массив d используется для хранения расстояний от начальной клетки до каждой из остальных. Если d[i][j] = 0, то расстояние до клетки (i,j) еще неизвестно. Клетки обрабатываются в порядке увеличения расстояния. Для этого используется очередь. При обработке клетки в очередь заносятся все соседние с ней, расстояние до которых еще не известно. Причем расстояние до них устанавливается равным расстоянию до текущей клетки плюс один. Рассмотрим работу алгоритма на приведенном выше примере. Порядок занесения клеток в очередь будет следующий: (1,0), (0,0), (2,0), (0,1), (3,0), (0,2), (3,1), (0,3), (1,2), (3,2), (0,4), (2,2), (0,5), (1,4), (1,5), (2,4), (2,5), (3,4), (3,5). Значения элементов массива d: ...... 2 3 4 5 6 7 s#.#.. 1 0 5 0 7 8 .#.#.. 2 0 6 0 8 9 ...#f. 3 4 5 0 9 10 Приведенный выше алгоритм находит только длину кратчайшего пути. Сам путь можно восстановить с конца с помощью массива d. Последней клеткой в пути будет (fi,fj). Предпоследняя клетка (i,j) - это одна из соседних с (fi,fj) клеток, для которой d[i][j] = d[fi][fj] - 1. Затем выбираем клетку, соседнюю с предпоследней, расстояние до которой равно d[fi][fj] - 2, и так далее, пока не дойдем до (si,sj).
Входной файл: input.txt | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Секунда | Преобразование | Колонии | |||||||
|---|---|---|---|---|---|---|---|---|---|
| 0-я | 1-я | 2-я | 3-я | 4-я | 5-я | 6-я | 7-я | ||
| начало | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | |
| 1-я | reproduce 2 5 | 1 | 1 | 5 | 1 | 1 | 1 | 1 | 1 |
| 2-я | copy 4 2 | 1 | 1 | 5 | 1 | 6 | 1 | 1 | 1 |
| 3-я | die 1 0 | 1 | 0 | 5 | 1 | 6 | 1 | 1 | 1 |
| 4-я | merry-go-round 0 0 | 1 | 1 | 0 | 5 | 1 | 6 | 1 | 1 |
| 5-я | teleport 5 3 | 1 | 1 | 0 | 0 | 1 | 4 | 1 | 1 |
| 6-я | swap 0 2 | 0 | 1 | 1 | 0 | 1 | 4 | 1 | 1 |
| 7-я | reproduce 2 5 | 0 | 1 | 5 | 0 | 1 | 4 | 1 | 1 |
| 8-я | copy 4 2 | 0 | 1 | 5 | 0 | 6 | 4 | 1 | 1 |
| 9-я | die 1 0 | 0 | 0 | 5 | 0 | 6 | 4 | 1 | 1 |
| 10-я | merry-go-round 0 0 | 1 | 0 | 0 | 5 | 0 | 6 | 4 | 1 |
| 11-я | teleport 5 3 | 1 | 0 | 0 | 0 | 0 | 4 | 4 | 1 |
На проверку принимаются решения, написанные на языке C++. Отправляйте свои письма по адресу accepted@narod.ru. Решение должно содержаться в одном файле, вложенном в ваше письмо. Для компиляции, по вашему выбору, будет использоваться программа bcc.exe (Borland C++ 3.1) или cl.exe (Microsoft Visual C++ 6.0). Размер исходного кода не должен превышать 40 Kb. Для каждой задачи отправляйте отдельное письмо. В теле письма укажите следующие данные (пример):
ник (*): vasya пароль (*): a193z458w2 фамилия: Иванов имя: Василий e-mail: vasya@hotmail.com номер задачи (*): 1 компилятор (*): cl
(*) обозначены обязательные для заполнения поля. Ник и пароль вы сами придумаете при отправке своего первого письма. В последующих письмах указывайте те же самые ник и пароль. Я пока не придумал, что делать, если два человека используют один и тот же ник. Так что, старайтесь придумать его так, чтобы никто до вас его еще не придумал :-) Если вы укажите свой e-mail, то с этой проблемой будет легче разобраться. В качестве компилятора укажите bcc или cl. И, главное, не забудьте указать номер задачи!
При проверке решений, используются стандартные правила acm. Их можно найти, например, на сайте http://acm.baylor.edu. Проверка решений осуществляется на наборе тестов, недоступных участникам. Решение считается принятым, если выдало верный ответ на каждом из тестов. Программа должна считывать данные из указанного в условии входного файла и выдавать результат в указанный в условии выходной файл. Она не должна работать с другими файлами или каталогами. Входной файл нельзя открывать для записи. При доступе к входному и выходному файлу вы должны предполагать, что они находятся в текущей директории. Не используйте консольный ввод или вывод.
Решения принимаются до субботы следующей недели (8 мая 2004) включительно. В воскресенье (9 мая 2004) вечером будут высланы результаты тестирования и авторские решения.
Ведущий рассылкой: Виктор Журавлев
Вопросы, пожелания, предложения пишите на мой
e-mail.
| http://subscribe.ru/
E-mail: ask@subscribe.ru |
Отписаться
Убрать рекламу |
| В избранное | ||