Разбор задачи: Откройте замок

Предложенный нами код решения:

 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, указывая, что целевая комбинация недостижима.


Ключевые моменты решения

  1. Поиск в ширину (BFS):
    • Обеспечивает минимальное количество шагов до достижения целевой комбинации.
    • Мы обрабатываем все комбинации текущего уровня, прежде чем перейти к следующему.
  2. Механизм вращения колёс:
    • Словарь move позволяет быстро находить новые значения при повороте вперёд или назад.
  3. Оптимизация через множество moved:
    • Исключает повторную проверку комбинаций.
    • Сокращает количество шагов.
  4. Ранний выход:
    • Если целевая комбинация достигается в процессе генерации, функция немедленно возвращает результат.

Пример работы алгоритма

Входные данные:

deadends = ["0201", "0101", "0102", "1212", "2002"]
target = "0202"

Процесс:

  1. Начало: q = ["0000"], moved = {"0201", "0101", "0102", "1212", "2002"}.
  2. Шаг 1: Повороты от 0000 дают ["1000", "9000", "0100", "0900", "0010", "0090", "0001", "0009"].
  3. Шаг 2: Проверяем все комбинации уровня 1. Продолжаем вращать и проверять.
  4. Целевая комбинация 0202 достигается через 6 шагов.

Выходные данные:

6

 


0

Вы должны Войти или Зарегистрироваться чтобы оставлять комментарии