Программа курса:
Разбор задачи: Откройте замок
Предложенный нами код решения:
def openLock(deadends, target):
moved, q, cnt, move = set(deadends), ["0000"], 0, {str(i): [str((i + 1) % 10), str((i - 1) % 10)] for i in range(10)}
if "0000" in moved:
return -1
while q:
new = []
cnt += 1
for s in q:
for i, c in enumerate(s):
for cur in (s[:i] + move[c][0] + s[i + 1:], s[:i] + move[c][1] + s[i + 1:]):
if cur not in moved:
if cur == target:
return cnt
new.append(cur)
moved.add(cur)
q = new
return -1Функция openLock решает задачу поиска минимального количества шагов для разблокировки замка, начиная с комбинации 0000 и избегая "мёртвых" комбинаций (deadends).
Рассмотрим её работу поэтапно.
1. Инициализация переменных
moved, q, cnt, move = set(deadends), ["0000"], 0, {str(i): [str((i + 1) % 10), str((i - 1) % 10)] for i in range(10)}
moved: преобразуем списокdeadendsв множество для быстрого поиска. Это содержит комбинации, которые нельзя посещать.q: список["0000"]инициализирует очередь для проверки возможных комбинаций. Мы начинаем с начальной комбинации.cnt: переменная-счётчик шагов, начинающаяся с 0.move: словарь, который задаёт правила вращения колёс. Для каждого числа от0до9хранится список из двух значений:str((i + 1) % 10)— значение при повороте колеса вперёд.str((i - 1) % 10)— значение при повороте колеса назад.
2. Проверка начальной комбинации
if "0000" in moved:
return -1
Если начальная комбинация находится в "мёртвых", то разблокировать замок невозможно, и функция сразу возвращает -1.
3. Основной цикл поиска
while q:
new = []
cnt += 1
while q: Цикл продолжается, пока есть комбинации для проверки.new = []: Список для хранения новых комбинаций, которые будут проверяться на следующей итерации.cnt += 1: На каждом шаге увеличивается счётчик, так как мы переходим к новому уровню поиска.
4. Генерация новых комбинаций
for s in q:
for i, c in enumerate(s):
for cur in (s[:i] + move[c][0] + s[i + 1:], s[:i] + move[c][1] + s[i + 1:]):
if cur not in moved:
if cur == target:
return cnt
new.append(cur)
moved.add(cur)
for s in q: Проходим по всем комбинациям, которые нужно проверить на текущем уровне.for i, c in enumerate(s): Перебираем каждую цифру в текущей комбинации.move[c][0]иmove[c][1]: Получаем новые значения при повороте колеса вперёд или назад.s[:i] + move[c][0] + s[i + 1:]: Формируем новую комбинацию, заменяяi-ю цифру.
5. Проверка новых комбинаций
if cur not in moved: Если новая комбинация не посещена ранее и не является мёртвой:if cur == target: Если комбинация совпадает с целевой, возвращаем количество шаговcnt.new.append(cur): Добавляем комбинацию в список для следующей итерации.moved.add(cur): Помечаем комбинацию как посещённую.
6. Переход к следующему уровню
q = new
После проверки всех комбинаций текущего уровня переходим к комбинациям следующего уровня.
7. Завершение работы
Если все комбинации исчерпаны и целевая комбинация не достигнута:
return -1
Функция возвращает -1, указывая, что целевая комбинация недостижима.
Ключевые моменты решения
- Поиск в ширину (BFS):
- Обеспечивает минимальное количество шагов до достижения целевой комбинации.
- Мы обрабатываем все комбинации текущего уровня, прежде чем перейти к следующему.
- Механизм вращения колёс:
- Словарь
moveпозволяет быстро находить новые значения при повороте вперёд или назад.
- Словарь
- Оптимизация через множество
moved:- Исключает повторную проверку комбинаций.
- Сокращает количество шагов.
- Ранний выход:
- Если целевая комбинация достигается в процессе генерации, функция немедленно возвращает результат.
Пример работы алгоритма
Входные данные:
deadends = ["0201", "0101", "0102", "1212", "2002"]
target = "0202"
Процесс:
- Начало:
q = ["0000"],moved = {"0201", "0101", "0102", "1212", "2002"}. - Шаг 1: Повороты от
0000дают["1000", "9000", "0100", "0900", "0010", "0090", "0001", "0009"]. - Шаг 2: Проверяем все комбинации уровня 1. Продолжаем вращать и проверять.
- Целевая комбинация
0202достигается через 6 шагов.
Выходные данные:
6
Вы должны Войти или Зарегистрироваться чтобы оставлять комментарии