|
|
|
"""Задание на расчет интервалов, и вычисление времени, который ученик
|
|
|
|
и учитель присуствовали на уроке одновременно."""
|
|
|
|
|
|
|
|
tests = [
|
|
|
|
{
|
|
|
|
"data": {
|
|
|
|
"lesson": [1594663200, 1594666800],
|
|
|
|
"pupil": [
|
|
|
|
1594663340,
|
|
|
|
1594663389,
|
|
|
|
1594663390,
|
|
|
|
1594663395,
|
|
|
|
1594663396,
|
|
|
|
1594666472,
|
|
|
|
],
|
|
|
|
"tutor": [1594663290, 1594663430, 1594663443, 1594666473],
|
|
|
|
},
|
|
|
|
"answer": 3117,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"data": {
|
|
|
|
"lesson": [1594702800, 1594706400],
|
|
|
|
"pupil": [
|
|
|
|
1594702789,
|
|
|
|
1594704500,
|
|
|
|
1594702807,
|
|
|
|
1594704542,
|
|
|
|
1594704512,
|
|
|
|
1594704513,
|
|
|
|
1594704564,
|
|
|
|
1594705150,
|
|
|
|
1594704581,
|
|
|
|
1594704582,
|
|
|
|
1594704734,
|
|
|
|
1594705009,
|
|
|
|
1594705095,
|
|
|
|
1594705096,
|
|
|
|
1594705106,
|
|
|
|
1594706480,
|
|
|
|
1594705158,
|
|
|
|
1594705773,
|
|
|
|
1594705849,
|
|
|
|
1594706480,
|
|
|
|
1594706500,
|
|
|
|
1594706875,
|
|
|
|
1594706502,
|
|
|
|
1594706503,
|
|
|
|
1594706524,
|
|
|
|
1594706524,
|
|
|
|
1594706579,
|
|
|
|
1594706641,
|
|
|
|
],
|
|
|
|
"tutor": [
|
|
|
|
1594700035,
|
|
|
|
1594700364,
|
|
|
|
1594702749,
|
|
|
|
1594705148,
|
|
|
|
1594705149,
|
|
|
|
1594706463,
|
|
|
|
],
|
|
|
|
},
|
|
|
|
"answer": 3577,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"data": {
|
|
|
|
"lesson": [1594692000, 1594695600],
|
|
|
|
"pupil": [1594692033, 1594696347],
|
|
|
|
"tutor": [1594692017, 1594692066, 1594692068, 1594696341],
|
|
|
|
},
|
|
|
|
"answer": 3565,
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
def get_smaller_interval(interval_A, interval_B) -> list:
|
|
|
|
"""Если один из интервалов включает в себя другой, возвращаем
|
|
|
|
меньший. Если интервалы не входят друг в дргуа, возвращаем пустой
|
|
|
|
интервал."""
|
|
|
|
if interval_A[0] <= interval_B[0] and interval_A[1] >= interval_B[1]:
|
|
|
|
return interval_B
|
|
|
|
|
|
|
|
if interval_B[0] <= interval_A[0] and interval_B[1] >= interval_A[1]:
|
|
|
|
return interval_A
|
|
|
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
|
|
# get_intervals_without_small_included_intervals
|
|
|
|
def absorb_small_included_intervals(intervals: list) -> list:
|
|
|
|
"""Возвращает список интервалов без вложенных интервалов.
|
|
|
|
|
|
|
|
Вложенный интервал - интервал меньшего размера, полностью входящий
|
|
|
|
в больший.
|
|
|
|
|
|
|
|
Стоит учитывать, что функция работает только если интервалы
|
|
|
|
совпадают по началу или концу, если интервалы входят друг в друга
|
|
|
|
частично -- мы их не обрабатываем.
|
|
|
|
|
|
|
|
"""
|
|
|
|
# формируем список из меньших интервалов, входящих в бОльшие
|
|
|
|
small_intervals = [
|
|
|
|
get_smaller_interval(interval, intervals[k])
|
|
|
|
for i, interval in enumerate(intervals)
|
|
|
|
for k in range(i + 1, len(intervals))
|
|
|
|
if get_smaller_interval(interval, intervals[k])
|
|
|
|
]
|
|
|
|
|
|
|
|
# в качестве результа у нас будет новый список, состоящий из
|
|
|
|
# принятого списка интервалов `intervals`, за исключением входящих меньших
|
|
|
|
# интервалов `small_intervals`
|
|
|
|
result = [i for i in intervals if i not in small_intervals]
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def get_union_of_partly_intersecting_intervals(interval_A, interval_B) -> list:
|
|
|
|
"""Если интервалы А и Б пересекаются, то возвращаем новый
|
|
|
|
интервал, включающий А и Б."""
|
|
|
|
# планирую поступать по тому же принципу что и выше: добавлять
|
|
|
|
# сумму в исходный список, итого получим список с пересечениями,
|
|
|
|
# который потом можно прогнать через поглатитель меньших
|
|
|
|
# интервалов
|
|
|
|
|
|
|
|
# если один из интервалов пустой, то пересечение будет пустым
|
|
|
|
if not interval_A or not interval_B:
|
|
|
|
return []
|
|
|
|
|
|
|
|
# если какой-то из инетрвалов имеет нулевую длину (начало=конец),
|
|
|
|
# то пересечение пустое
|
|
|
|
if interval_A[0] == interval_A[1] or interval_B[0] == interval_B[1]:
|
|
|
|
return []
|
|
|
|
|
|
|
|
# нужно проверить, что интервалы вообще пересекаются
|
|
|
|
# А и B не пересекаются, если
|
|
|
|
# начало А > конец B, или если начало B > конец А
|
|
|
|
if interval_A[0] > interval_B[1] or interval_B[0] > interval_A[1]:
|
|
|
|
return []
|
|
|
|
|
|
|
|
# сумма интервалов: наименьшее начало из А и Б, и наибольший конец
|
|
|
|
# из А и Б
|
|
|
|
interval_begin = min(interval_A[0], interval_B[0])
|
|
|
|
interval_end = max(interval_A[1], interval_B[1])
|
|
|
|
return [interval_begin, interval_end]
|
|
|
|
|
|
|
|
|
|
|
|
def get_extended_intervals(intervals) -> list:
|
|
|
|
"""Возвращает расширенные интервалы из списка интервалов.
|
|
|
|
|
|
|
|
Расширенный интервал, это интервал, который есть сумма двух
|
|
|
|
пересекающихся инетрвалов."""
|
|
|
|
extended_intervals = [
|
|
|
|
get_union_of_partly_intersecting_intervals(interval, intervals[k])
|
|
|
|
for i, interval in enumerate(intervals)
|
|
|
|
for k in range(i + 1, len(intervals))
|
|
|
|
if get_union_of_partly_intersecting_intervals(interval, intervals[k])
|
|
|
|
]
|
|
|
|
|
|
|
|
# список extended_intervals может содержать дублирующиеся
|
|
|
|
# интервалы из списка intervals, поэтому мы возвращаем только
|
|
|
|
# отсутствующие в intevals значения
|
|
|
|
uniq_extended_intervals = [i for i in extended_intervals if i not in intervals]
|
|
|
|
return uniq_extended_intervals
|
|
|
|
|
|
|
|
|
|
|
|
def get_intersection(interval_A, interval_B) -> list:
|
|
|
|
# если один из интервалов пустой, то пересечение будет пустым
|
|
|
|
if not interval_A or not interval_B:
|
|
|
|
return []
|
|
|
|
|
|
|
|
# если какой-то из интервалов имеет нулевую длину (начало=конец),
|
|
|
|
# то пересечение пустое
|
|
|
|
if interval_A[0] == interval_A[1] or interval_B[0] == interval_B[1]:
|
|
|
|
return []
|
|
|
|
|
|
|
|
# нужно проверить, что интервалы вообще пересекаются
|
|
|
|
# А и Б не пересекаются, если
|
|
|
|
# начало А > конец Б, или если начало Б > конец А
|
|
|
|
if interval_A[0] > interval_B[1] or interval_B[0] > interval_A[1]:
|
|
|
|
return []
|
|
|
|
|
|
|
|
# начало интервала это наименьший из начал интервалов А и Б
|
|
|
|
# конец интервала это наименьший из концов интервалов А и Б
|
|
|
|
interval_begin = max(interval_A[0], interval_B[0])
|
|
|
|
interval_end = min(interval_A[1], interval_B[1])
|
|
|
|
return [interval_begin, interval_end]
|
|
|
|
|
|
|
|
|
|
|
|
def normilize_intervals(intervals) -> list:
|
|
|
|
"""Разбивает полученный список на пары, расширяет интервалы,
|
|
|
|
убирает вложенные интервалы."""
|
|
|
|
|
|
|
|
# из списка [1,2,3,4] формируем [[1,2], [3,4]]
|
|
|
|
intervals_ = [intervals[i : i + 2] for i in range(0, len(intervals), 2)]
|
|
|
|
|
|
|
|
# получаем расширенные интервалы
|
|
|
|
extended_intervals = get_extended_intervals(intervals_)
|
|
|
|
|
|
|
|
# убираем вложенные инетрвалы
|
|
|
|
n_intervals = absorb_small_included_intervals(intervals_ + extended_intervals)
|
|
|
|
return n_intervals
|
|
|
|
|
|
|
|
|
|
|
|
def appearance(intervals) -> int:
|
|
|
|
"""Возвращает время одновременного присуствия ученика и учителя на
|
|
|
|
уроке в секундах."""
|
|
|
|
lesson = intervals["lesson"]
|
|
|
|
pupil = normilize_intervals(intervals["pupil"])
|
|
|
|
tutor = normilize_intervals(intervals["tutor"])
|
|
|
|
|
|
|
|
# получаем все интервалы студента на уроке
|
|
|
|
pupil_on_lesson = [
|
|
|
|
get_intersection(lesson, p) for p in pupil if get_intersection(lesson, p)
|
|
|
|
]
|
|
|
|
|
|
|
|
# по очереди сравниваем каждый из интервалов преподавателя с
|
|
|
|
# каждым из интервалов пересечения студента на уроке
|
|
|
|
tutor_on_pupil = [
|
|
|
|
get_intersection(pol, t) for t in tutor for pol in pupil_on_lesson
|
|
|
|
]
|
|
|
|
|
|
|
|
# создаем список из продолжительности каждого интервала, если он
|
|
|
|
# не пустой, а затем суммируем все элементы
|
|
|
|
answer = sum([intr[1] - intr[0] for intr in tutor_on_pupil if intr])
|
|
|
|
return answer
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
for i, test in enumerate(tests):
|
|
|
|
test_answer = appearance(test["data"])
|
|
|
|
assert (
|
|
|
|
test_answer == test["answer"]
|
|
|
|
), f'Error on test case {i}, got {test_answer}, expected {test["answer"]}'
|