Программа курса:
Разбор задачи: Оценивайте деление
Предложенный нами код решения:
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, которая принимает:
equations — список пар переменных, представляющих уравнения, например:
equations = [["a", "b"], ["b", "c"]]Это означает, что
a / b = 2.0иb / c = 3.0.values — список вещественных чисел, соответствующих значениям делений, например:
values = [2.0, 3.0]Соответствие:
a / b = 2.0b / c = 3.0
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в графе и возвращает произведение весов всех рёбер на этом пути. - Разберём алгоритм подробно:
- Проверка существования узлов:
- Если хотя бы одна из переменных (
startилиend) отсутствует в графе, мы сразу возвращаем-1.0.
- Если хотя бы одна из переменных (
Инициализация очереди:
- Используется очередь
dequeдля хранения текущих узлов и накопленного результата (product), который представляет произведение всех весов на пути.
Пример:
queue = deque([(start, 1.0)])- Используется очередь
- Обход графа:
Пока очередь не пуста:
- Извлекаем текущий узел
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.
- На первом шаге:
- Извлекаем текущий узел
- Если соседнего узла ещё не посещали, добавляем его в очередь с обновленным результатом.
- Возврат результата:
- Если путь не найден, возвращаем
-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"]]
Построение графа:
graph = { "a": {"b": 2.0}, "b": {"a": 0.5, "c": 3.0}, "c": {"b": 0.333} }- Обработка запросов:
"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.
- Переменная