Разбор задачи: Оценивайте деление

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

 from collections import defaultdict, deque

def calcEquation(equations, values, queries):
    graph = defaultdict(dict)
    for (a, b), value in zip(equations, values):
        graph[a][b] = value
        graph[b][a] = 1 / value

    def bfs(start, end):
        if start not in graph or end not in graph:
            return -1.0
        queue = deque([(start, 1.0)])
        visited = set()

        while queue:
            current, product = queue.popleft()
            if current == end:
                return product
            visited.add(current)
            for neighbor, value in graph[current].items():
                if neighbor not in visited:
                    queue.append((neighbor, product * value))
        return -1.0

    return [bfs(start, end) for start, end in queries]

Подробный разбор решения задачи


Постановка задачи

Нам нужно реализовать функцию calcEquation, которая принимает:

  1. equations — список пар переменных, представляющих уравнения, например:

    equations = [["a", "b"], ["b", "c"]]
    

    Это означает, что a / b = 2.0 и b / c = 3.0.

  2. values — список вещественных чисел, соответствующих значениям делений, например:

    values = [2.0, 3.0]
    

    Соответствие:

    • a / b = 2.0
    • b / c = 3.0
  3. queries — список запросов, где каждый запрос требует найти результат деления одной переменной на другую, например:

    queries = [["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"]]
    

    Если результат невозможно определить (например, одна или обе переменные отсутствуют), мы должны вернуть -1.0.


Решение задачи

Для решения задачи мы используем представление входных данных в виде графа. Каждая переменная становится узлом, а деления между переменными представляются рёбрами с весами (значениями делений).


1. Построение графа

graph = defaultdict(dict)
for (a, b), value in zip(equations, values):
    graph[a][b] = value
    graph[b][a] = 1 / value
  • Что делает этот код?
    • Мы проходим по каждой паре переменных (a, b) из equations и их значению value из values.
    • Для каждой пары переменных:
      • Добавляем ребро от a к b с весом value (a / b = value).
      • Добавляем обратное ребро от b к a с весом 1 / value (b / a = 1 / value).
  • Пример построения графа:
    Для входных данных:

    equations = [["a", "b"], ["b", "c"]]
    values = [2.0, 3.0]
    

    Получится граф:

    a -> b (2.0), b -> a (0.5)
    b -> c (3.0), c -> b (0.333)
    
  • Структура графа:
    Граф представлен как словарь, где:

    • Ключи — это переменные (узлы графа).
    • Значения — словари, представляющие соседей узла и веса рёбер.

    Пример:

    graph = {
        "a": {"b": 2.0},
        "b": {"a": 0.5, "c": 3.0},
        "c": {"b": 0.333}
    }
    

2. Поиск пути в графе

def bfs(start, end):
    if start not in graph or end not in graph:
        return -1.0
    queue = deque([(start, 1.0)])
    visited = set()

    while queue:
        current, product = queue.popleft()
        if current == end:
            return product
        visited.add(current)
        for neighbor, value in graph[current].items():
            if neighbor not in visited:
                queue.append((neighbor, product * value))
    return -1.0
  • Что делает функция? Функция bfs (поиск в ширину) ищет путь от узла start к узлу end в графе и возвращает произведение весов всех рёбер на этом пути.
  • Разберём алгоритм подробно:
    1. Проверка существования узлов:
      • Если хотя бы одна из переменных (start или end) отсутствует в графе, мы сразу возвращаем -1.0.
    2. Инициализация очереди:

      • Используется очередь deque для хранения текущих узлов и накопленного результата (product), который представляет произведение всех весов на пути.

      Пример:

      queue = deque([(start, 1.0)])
      
    3. Обход графа:
      • Пока очередь не пуста:

        • Извлекаем текущий узел current и его накопленный результат product.
        • Если текущий узел совпадает с end, возвращаем накопленный результат.

        Пример:

        • Для запроса "a / c":
          • На первом шаге: current = "a", product = 1.0.
          • Переходим к "b", обновляем product = 1.0 * 2.0 = 2.0.
          • Переходим к "c", обновляем product = 2.0 * 3.0 = 6.0.
      • Если соседнего узла ещё не посещали, добавляем его в очередь с обновленным результатом.
    4. Возврат результата:
      • Если путь не найден, возвращаем -1.0.

3. Обработка всех запросов

return [bfs(start, end) for start, end in queries]
  • Мы вызываем функцию bfs для каждого запроса (start, end) из queries и собираем результаты в список.

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

Рассмотрим пример:

equations = [["a", "b"], ["b", "c"]]
values = [2.0, 3.0]
queries = [["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"]]
  1. Построение графа:

    graph = {
        "a": {"b": 2.0},
        "b": {"a": 0.5, "c": 3.0},
        "c": {"b": 0.333}
    }
    
  2. Обработка запросов:
    • "a / c":
      • Путь: "a" -> "b" -> "c".
      • Результат: 2.0 * 3.0 = 6.0.
    • "b / a":
      • Путь: "b" -> "a".
      • Результат: 0.5.
    • "a / e":
      • Переменная e отсутствует.
      • Результат: -1.0.
    • "a / a":
      • Деление на себя.
      • Результат: 1.0.
    • "x / x":
      • Переменная x отсутствует.
      • Результат: -1.0.

 



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