Искусственный интеллект. Всероссийская олимпиада школьников в Москве (2025-2026 год)

 

  Главная      Книги - Разные 

 

поиск по сайту            правообладателям  

    

 

   

 

   

 

 

 

 

Искусственный интеллект. Всероссийская олимпиада школьников в Москве (2025-2026 год)

 

 

Разбор заданий школьного этапа ВсОШ 2025/26 по искусственному интеллекту
5-6 классы
Максимальное количество баллов за олимпиаду - 84
Задание 1. На экране у робота было число 1. По команде СЛЕДУЮЩЕЕ робот увеличивает число
на экране на 1, а по команде УДВОЙ робот умножает число на экране на 2. Назовём число дости-
жимым, если оно могло оказаться на экране в результате последовательного выполнения четырёх
команд. Сколько существует достижимых чисел?
Ответ: 8
Критерий оценивания: точное совпадение ответа - 12 баллов
Максимальный балл за задание - 12
Решение.
Идём по шагам.
После 1-й команды из числа 1 в любом случае получаем 2.
После 2-й: из 2 можно получить 3 (СЛЕДУЮЩЕЕ) или 4 (УДВОЙ).
После 3-й: из 3 получаем 4 или 6, из 4 получаем 5 или 8. Итак, возможны 4, 5, 6, 8.
После 4-й:
4 → 5 или 8,
5 → 6 или 10,
6 → 7 или 12,
8 → 9 или 16.
Значит, все возможные числа: 5, 6, 7, 8, 9, 10, 12, 16 - всего 8.
Задание 2. Формула суммы первых n натуральных чисел имеет вид
Задание 3. В турнире участвовали пять моделей ИИ, будем называть их игроками. Каждая пара
игроков сыграла ровно один матч (всего было проведено 10 матчей). По правилам игроку за победу
даётся 2 очка, за ничью - 1 очко, за поражение - 0 очков. Программа, которая подсчитывала
набранные очки, по ошибке за победу начисляла 3 очка, очки за ничью и за поражение она начисляла
в соответствии с правилами. По составленной ею таблице сумма очков всех игроков оказалась равна
24. Сколько матчей на турнире закончились вничью?
Ответ: 6
Критерий оценивания: точное совпадение ответа - 12 баллов
Максимальный балл за задание - 12
Решение.
По правильным правилам любой матч (победа 2:0 или ничья 1:1) даёт в сумме 2 очка, значит
всего должно быть 10 · 2 = 20 очков.
1
Разбор заданий школьного этапа ВсОШ 2025/26 по искусственному интеллекту
5-6 классы
Программа завышала на 1 очко каждый матч с победителем, поэтому общая сумма стала больше
на число таких матчей:
24 - 20 = 4.
Значит, было 4 матча с победой и 10 - 4 = 6 ничьих.
Задание 4. Модели ИИ нужно последовательно выдать пять различных задач, и две из которых -
тяжёлые. Планировщик экспериментов задаёт порядок выдачи этих пяти задач так, чтобы тяжёлые
задачи не выдавались подряд. Сколькими способами он может это сделать?
Ответ: 72
Критерий оценивания: точное совпадение ответа - 12 баллов
Максимальный балл за задание - 12
Решение.
Всего порядков пяти разных задач: 5! = 120.
Посчитаем, сколько «плохих» порядков, где две тяжёлые стоят рядом. Считаем их «блоком».
Тогда имеем: блок и три обычные задачи - всего 4 объекта, их можно переставить 4! = 24 способами.
Внутри блока тяжёлые могут стоять в двух порядках, значит «плохих» порядков 24 · 2 = 48.
Требуемые порядки: 120 - 48 = 72.
Задание 5. Нейронная сеть на телефоне у программиста ставит будильник на 9 часов утра каждый
третий день, начиная с 1 июня (т.е. 1-го июня, 4-го июня, 7-го июня и т.д.). Будильник на 10 часов
утра она ставит каждое пятое число месяца, начиная со 2-го июня (т.е. 2-го июня, 7-го июня, 12-го
июня и т.д.). В июне 30 дней. Сколько в июне будет дней, на которые будет установлен ровно один
будильник?
Ответ: 12
Критерий оценивания: точное совпадение ответа - 12 баллов
Максимальный балл за задание - 12
Решение.
Дни «каждый третий» от 1 июня: 1, 4, 7, 10, 13, 16, 19, 22, 25, 28 - всего 10 дней. Дни «каждое
пятое» от 2 июня: 2, 7, 12, 17, 22, 27 - всего 6 дней.
Совпадают только 7 и 22 (каждые 15 дней), их 2.
Тогда ровно один будильник стоит на
(10 - 2) + (6 - 2) = 8 + 4 = 12
днях.
Задание 6. План города представляет собой квадрат 10×10, вертикальные и горизонтальные улицы
(линии сетки) пронумерованы числами от 0 до 10 (слева направо и снизу вверх). Робот стартует на
пересечении 0-горизонтальной и 0-вертикальной улиц, он должен отвезти заказ на пересечение 3-
горизонтальной и 3-вертикальной улиц.
Робот выбирает кратчайший маршрут, значит он двигается только вверх и вправо. Известно, что
на пересечении 2-горизонтальной и 2-вертикальной улиц ведутся работы, через этот перекрёсток
проехать нельзя. Сколько различных маршрутов может выбрать робот?
Ответ: 8
Критерий оценивания: точное совпадение ответа - 12 баллов
Максимальный балл за задание - 12
Решение.
В финиш (3, 3) можно попасть только из (3, 2) (сверху) или из (2, 3) (слева).
- Все допустимые пути в (3, 2) - это пути в (3, 1) (пути через (2, 2) запрещены). - Аналогично,
все допустимые пути в (2, 3) - это пути в (1, 3).
Считаем их: от (0, 0) до (3, 1) нужно сделать 3 шага вправо и 1 вверх - место для этого един-
ственного шага вверх можно выбрать 4 способами, значит путей 4. От (0, 0) до (1, 3) симметрично
тоже 4 пути.
Итого путей в (3, 3): 4 + 4 = 8.
2
Разбор заданий школьного этапа ВсОШ 2025/26 по искусственному интеллекту
5-6 классы
Задание 7. Программа-помощник отвечает на вопросы по урокам. Ведётся журнал сообщений, в
который записывается следующая информация.
1) Кто задал вопрос (Имя автора).
2) Понятен ли смысл вопроса (Поняла ли смысл?).
3) Тема вопроса (Тема).
4) Количество слов в вопросе (Длина вопроса).
5) Время отправки (Время).
В результате работы программы в тестовом режиме в течение дня журнал сообщений представ-
ляет собой приведённую ниже таблицу.
Имя автора
Поняла ли смысл?
Тема
Длина вопроса
Время
Ася
Да
расписание
12
?
Боря
Нет
?
14
день
Вера
Нет
столовая
9
день
Глеб
Да
расписание
?
утро
Дина
?
кружки
13
вечер
Егор
Да
домашка
10
утро
Зоя
Нет
домашка
8
вечер
Илья
Да
расписание
11
утро
В каждом из четырёх столбцов, кроме первого, оказалось ровно по одному пропуску, отмеченному
знаком вопроса. Чтобы подвести итоги работы программы, нужно заполнить пропуски, руководству-
ясь следующими правилами.
Во втором, третьем и пятом слотбце нужно поставить тот вариант ответа, который чаще всего
встречается среди уже заполненных значений этого столбца. Если несколько вариантов встречаются
одинаковое количество раз мы берем первый из них в алфавитном порядке.
В столбце длина вопроса нужно поставить вместо пропуска среднее из уже заполненных чисел.
То есть такое число, больше которого и меньше которого одинаковое количество чисел в данном
столбце.
Заполните все пропуски согласно приведённым правилам.
Ответ: Да; расписание; 11; утро
Критерий оценивания:
-3
За каждый верный ответ
балла .
Максимальный балл за задание - 12
Решение.
Считаем по столбцам.
- «Поняла ли смысл?»: среди заполненных стоит «Да» у четырёх записей, «Нет» - у трёх,
значит подставляем «Да».
- «Тема»: чаще всего встречается «расписание» (3 раза), значит подставляем «расписание».
- «Время»: «утро» встречается 3 раза, «день» - 2, «вечер» - 2; берём «утро».
- «Длина вопроса»: сложим известные длины 12 + 14 + 9 + 13 + 10 + 8 + 11 = 77. Их семь, среднее
77 : 7 = 11. Поэтому в пропуск ставим 11.
3
Разбор заданий школьного этапа ВсОШ 2025/26 по искусственному интеллекту
7-8 классы
Максимальное количество баллов за олимпиаду - 100
Следовательно, наибольшее возможное значение медианы A ∪ B равно 99.5.
Задание 2. Три программы ИИ (искусственного интеллекта), отвечают на одни и те же 30 вопросов.
Ансамбль из трёх программ выдаёт ответ на каждый вопрос «по правилу большинства»: если хотя
бы две из трёх программ дали одинаковый ответ, выдаётся именно этот ответ. В противном случае
выдаётся ответ первой программы. Известно, что каждая из трёх программ ответила верно не менее
чем на 22 вопроса. Какое наименьшее число правильных ответов гарантированно будет у ансамбля
из трёх программ?
Ответ: 18
Критерий оценивания: точное совпадение ответа - 12 баллов
Максимальный балл за задание - 12
Решение.
Каждая программа ошибается не более чем на 8 из 30 вопросов, поэтому суммарное число ошибок
трёх программ не превосходит
8 + 8 + 8 = 24.
1
Разбор заданий школьного этапа ВсОШ 2025/26 по искусственному интеллекту
7-8 классы
Если ансамбль на каком-то вопросе выдаёт неверный ответ, то хотя бы две программы на этом
вопросе ошиблись (либо две согласованно дали один и тот же неверный ответ, либо все трое ответили
по-разному и тогда выбор ответа первой программы неверен, но и здесь не менее двух программ
ошиблись). Значит, число вопросов, на которых ошибся ансамбль, не больше, чем
24
= 12.
2
Следовательно, минимум гарантированных верных ответов ансамбля равен
30 - 12 = 18.
Покажем, что оценка точна. Разобьём 12 вопросов на три группы по 4:
• на первых четырёх одинаково ошибаются первая и вторая программы (третья отвечает верно);
• на следующих четырёх одинаково ошибаются первая и третья;
• на ещё четырёх одинаково ошибаются вторая и третья.
Тогда каждая программа ошиблась ровно 8 раз, а на всех этих 12 вопросах ансамбль выбирает
неверное «большинство». На остальных 18 вопросах все три программы отвечают верно. В итоге у
ансамбля ровно 18 верных ответов, что совпадает с найденной нижней границей.
Итак, наименьшее гарантированное число правильных ответов ансамбля равно 18.
Задание 3. В порту расположено m площадок, пронумерованных целыми числами 0, 1, 2, . . . , m - 1.
В порт прибудет контейнеровоз с сотней контейнеров, которые пронумерованы последовательными
целыми числами. Автоматический кран разгружает контейнеровоз по следующему правилу: контей-
нер с номером N разгружается на площадку с таким номером k, что N - k делится на m (напомним,
что k принимает значения от 0 до m - 1). При каком наибольшем значении m мы гарантированно
можем утверждать, что на каждой площадке после разгрузки окажется не менее 9 контейнеров?
Ответ: 11
Критерий оценивания: точное совпадение ответа - 12 баллов
Максимальный балл за задание - 12
Решение.
По правилу разгрузки каждый контейнер идёт на площадку по своему остатку по модулю m.
Разбор заданий школьного этапа ВсОШ 2025/26 по искусственному интеллекту
7-8 классы
Ответ: 130
Критерий оценивания: точное совпадение ответа - 12 баллов
Максимальный балл за задание - 12
Решение.
Рис. 1: Точки на плоскости
Новую точку (0, 0), помеченную звёздочкой, нужно отнести к одному из трёх классов по правилу
ближайшего центра.
Назовём центром класса точку, у которой первая координата равна среднему арифметическому
всех первых координат точек класса, а вторая координата - среднему арифметическому всех вторых
3
Разбор заданий школьного этапа ВсОШ 2025/26 по искусственному интеллекту
7-8 классы
координат. Ближайшим для точки считается класс, расстояние до центра которого наименьшее; при
равенстве расстояний выбирается класс с меньшим номером.
Правило ближайшего центра требует относить точку к ближайшему для неё классу. Определите,
какой класс получит точка, помеченная звёздочкой.
Ответ: 1
Критерий оценивания: точное совпадение ответа - 12 баллов
Максимальный балл за задание - 12
Решение.
Посчитаем центры классов:
4
n = int(input())
a = int(input())
b = int(input())
p = int(input())
q = int(input())
m = int(input())
if n == 0:
print(a)
elif n == 1:
print(b)
else:
f0, f1 = a, b
for _ in range(2, n + 1):
f0, f1 = f1, (p ∗ f1 + q ∗ f0) % m
print(f1)
Задание 7.
Пожарная тревога Имя входного файла: стандартный ввод
Имя выходного файла: стандартный вывод
Ограничения по времени: 1 секунда
Ограничения по памяти: 256 мегабайт
Представь, что у тебя есть карта города. На ней есть две пожарные станции: станция класса
(0) с координатами (x0, y0) и станция класса (1) с координатами (x1, y1). Где-то в городе произошла
авария, её место обозначено точкой (x, y).
Чтобы вызвать помощь, нужно понять, какая станция доберётся до аварии быстрее. Машины
пожарных двигаются только вдоль улиц и проспектов, то есть сначала по горизонтали, потом по
вертикали (или наоборот). Время проезда считается просто: складываем количество кварталов по
горизонтали и по вертикали. Именно поэтому расстояние от точки ((u,v)) до точки (r, s) определяется
так:
Разбор заданий школьного этапа ВсОШ 2025/26 по искусственному интеллекту
7-8 классы
Посчитаем манхэттенские расстояния от точки аварии до каждой станции:
x = int(input())
y = int(input())
x0 = int(input())
y0 = int(input())
x1 = int(input())
y1 = int(input())
d0 = abs(x - x0) + abs(y - y0)
d1 = abs(x - x1) + abs(y - y1)
print(0 if d0 <= d1 else 1)
6
Разбор заданий школьного этапа ВсОШ 2025/26 по искусственному интеллекту
9-11 классы
Максимальное количество баллов за олимпиаду - 112
Задание 1. Пусть k >= 2 - целое число. Будем говорить, что две точки на числовой прямой дружат,
если разность их координат делится на k. Непустое множество точек назовём кластером, если любые
две точки в этом множестве дружат, и к нему нельзя добавить ни одной точки, чтобы это свойство
сохранилось.
При каких целых k >= 2 точки числовой прямой с координатами
1, 7, 21, 22, 28, 42, 43, 49, 63
можно разбить на два кластера?
Ответ: 2, 3, 7
Критерий оценивания: Точное совпадение ответа - 12 баллов
Максимальный балл за задание - 12
Решение.
Задание 3.
В заповеднике планируют ввести автоматический мониторинг редких розовых лемуров. Для те-
стирования системы была выбрана группа из 100 животных, в которой 10% составляют розовые
1
Разбор заданий школьного этапа ВсОШ 2025/26 по искусственному интеллекту
9-11 классы
лемуры (класс 1), а оставшиеся 90% - обычные лемуры (класс 0). Будем называть эту группу те-
стовой выборкой.
Модели ИИ для каждой особи из тестовой выборки сделали предсказание, к какому классу (0
или 1) относится данный лемур. Для оценки качества распознования введём следующие стандартные
обозначения:
Разбор заданий школьного этапа ВсОШ 2025/26 по искусственному интеллекту
9-11 классы
Задание 4. Вася тестировал модель с действительными параметрами x и y. Он выяснил, что функ-
ция потерь задаётся формулой
Задание 5. Есть набор примеров для обучения: всего N примеров, из них ровно два - положи-
тельные, остальные - отрицательные. Случайно выбирают группу из 4 примеров. Известно, что
вероятность того, что в группе окажутся оба положительных, в 2 раза больше вероятности того, что
не окажется ни одного положительного. Найдите все возможные значения N.
Ответ: 7
Критерий оценивания: Точное совпадение ответа - 12 баллов
Максимальный балл за задание - 12
Решение.
Пусть отрицательных m = N - 2.
Число 4-элем. групп:
Задание 6.
Отчёт об обучении нейросетистандартный вводстандартный вывод1 секунда256 мегабайт
3
Разбор заданий школьного этапа ВсОШ 2025/26 по искусственному интеллекту
9-11 классы
Обучение нейронных сетей обычно делится на эпохи. За одну эпоху, в рамках обучения, модель
один раз проходится по обучающему датасету.
Дима долгое время работал над новой разработкой. В процессе её обучения было целых n эпох.
После каждой эпохи, Дима записывал текущее время в формате HH : MM (например, 13 : 03).
Теперь Диме интересно, какое минимальное количество времени могло уйти на обучение модели.
Дима сейчас очень загружен рабочими задачами, поэтому обратился за помощью к Вам. Посчи-
тайте минимальное количество времени, которое могло уйти на обучение модели.
Формат входных данных
n = int(input())
prev = None
s=0
day = 24 ∗ 60
for _ in range(n):
hh, mm = map(int, input().strip().split(’:’))
t = hh ∗ 60 + mm
if prev is None:
prev = t
continue
if t >= prev:
s += t - prev
else:
s += day - prev + t
prev = t
print(s - 1)
Задание 7.
Специальное дерево принятия решений
Имя входного файла: стандартный ввод
Имя выходного файла: стандартный вывод
Ограничения по времени: 1 секунда
Ограничения по памяти: 256 мегабайт
Дано полное бинарное дерево принятия решений глубины n. От корня до листа делается ровно
n шагов. На каждом шаге принимается одно из двух решений: пойти влево или вправо. Изначально
оба варианта равновероятны: у каждого ребра вероятность 1/2.
4
Разбор заданий школьного этапа ВсОШ 2025/26 по искусственному интеллекту
9-11 классы
Кто-то изменил устройство дерева и поменял две вероятности на ребрах на 0:
• первое ребро - это a-е ребро на пути «всегда влево» (ребро между уровнями a - 1 и a, если от
корня на каждом шаге выбирать влево);
• второе ребро - это b-е ребро на пути «всегда вправо» (ребро между уровнями b - 1 и b, если
от корня на каждом шаге выбирать вправо).
Все остальные ребра по-прежнему имеют вероятность 1/2 (кроме тех ребер, которые лишились
соседнего ребра, у них вероятность теперь равна единице).
Исходами в этом дереве называются листы (вершины на самом нижнем уровне). Вероятность
каждого исхода, это произведение вероятностей на пути до соответствующего листа.
Требуется определить, сколько различных исходов (листов дерева) всё ещё имеют ненулевую
вероятность.
В первом тестовом примере, доступными останется лишь 2 исхода.
Решение.
В полном двоичном дереве глубины n листьев 2n. Если обнулить ребро на пути «всегда влево» на
уровне a, то все пути, проходящие через него, становятся невозможными - это ровно 2n-a листьев
под этим ребром. Аналогично, обнуление ребра на пути «всегда вправо» на уровне b убирает ещё 2n-b
листьев. Эти множества не пересекаются (нельзя одновременно идти всё время влево и всё время
вправо в начале пути). Итого остаётся
Для каждой строки вычисляется взвешенная сумма
6
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Первый тур, 17 января 2026 года
A. Натуральные числа
Баллов за задачу:
50
Формат сдачи ответа:
ввод ответа
Количество попыток:
10
Посылка в зачет:
последняя
Условие
В ряд выписали 100 натуральных чисел по очереди. Второе число было равно 1,
а каждое число, начиная с третьего, равно сумме всех предыдущих выписанных
чисел. Затем первое число стерли. Оказалось, что одно из оставшихся чисел равно
123456123456123456. Чему могло быть равно стертое число?
Формат вывода
В качестве ответа выведите все подходящие значения в порядке возрастания через
пробел.
Система оценивания
Точное совпадение ответа - 50 баллов.
Результаты тестирования не доступны во время проведения тура.
Страница 1 из 12
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Первый тур, 17 января 2026 года
B. Матрицы и забытые активации
Баллов за задачу:
50
Формат сдачи ответа:
ввод ответа
Количество попыток:
10
Посылка в зачет:
последняя
Условие
Вася учится рисовать картинки с помощью нейросети: каждой точке на плоскости
он хочет сопоставить цвет пикселя в трёх каналах (R, G, B).
Формат вывода
В качестве ответа выведите три числа через пробелы. Если получится нецелое число,
выведите его с точностью до 6 знаков после запятой. Если ответа нет или существует
несколько возможных выведите -1.
Страница 2 из 12
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Первый тур, 17 января 2026 года
Система оценивания
Точное совпадение ответа - 50 баллов.
Результаты тестирования не доступны во время проведения тура.
Страница 3 из 12
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Первый тур, 17 января 2026 года
C. Среднее и медиана
Баллов за задачу:
50
Формат сдачи ответа:
ввод ответа
Количество попыток:
10
Посылка в зачет:
последняя
Условие
при описанных условиях.
Формат вывода
Ответ округлите до 6 знаков после запятой, используя в качестве разделителя точку.
Система оценивания
Точное совпадение ответа - 50 баллов.
Результаты тестирования не доступны во время проведения тура.
Страница 4 из 12
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Первый тур, 17 января 2026 года
D. MAE
Баллов за задачу:
50
Формат сдачи ответа:
ввод ответа
Количество попыток:
10
Посылка в зачет:
последняя
Условие
Формат вывода
Ответ округлите до 6 знаков после запятой, используя в качестве разделителя точку.
Система оценивания
Точное совпадение ответа - 50 баллов.
Результаты тестирования не доступны во время проведения тура.
Страница 5 из 12
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Первый тур, 17 января 2026 года
E. Прямая крутится
Баллов за задачу:
50
Формат сдачи ответа:
ввод ответа
Количество попыток:
10
Посылка в зачет:
последняя
Условие
Представим, что у нас есть прямоугольник, раскрашенный в два цвета: часть его
площади белая, часть - чёрная. Нам нужно классифицировать точки внутри пря-
моугольника по цвету.
Мы используем очень простой классификатор: проводим через центр прямоугольни-
ка прямую L. Всё, что лежит по одну сторону от этой прямой, считаем чёрным, а
всё, что по другую сторону, считаем белым.
Теперь посмотрим, как хорошо такая прямая может “угадать” разметку. Для лю-
бой выбранной прямой L можно вычислить долю площади, где предсказанный цвет
совпадает с настоящим.
Рассмотрим пример квадрата 2×2. Легко видеть, что доля правильно предсказанной
площади равна 0.75.
Среди всех прямых L, проходящих через центр прямоугольника, какое наибольшее
значение может принимать доля площади, предсказанной правильно? Посчитайте
ответы для каждой из трех картинок ниже.
Страница 6 из 12
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Первый тур, 17 января 2026 года
Формат вывода
Выпишите через пробел ответы для трёх картинок. Ответы округлите до 6 знаков
после запятой, используя в качестве разделителя точку.
Система оценивания
Совпадение всех трех ответов - 50 баллов.
Совпадение двух ответов - 25 баллов.
Результаты тестирования не доступны во время проведения тура.
Страница 7 из 12
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Первый тур, 17 января 2026 года
F. NLP
Баллов за задачу:
50
Формат сдачи ответа:
ввод ответа
Количество попыток:
10
Посылка в зачет:
последняя
Условие
Петя изучает язык, в котором алфавит состоит из букв:
{A, D, E, I, L, M, N, S, T }.
Изначально написано слово DS.
Затем на каждом шаге к слову справа приписывается ещё одна буква.
Выбор новой буквы зависит исключительно от последней буквы текущего слова.
Правила для приписывания новой буквы такие:
• Если последняя буква - A равновероятно добавляется одна из {M, D}.
• Если последняя буква E или I равновероятно добавляется одна из {T, S, M}.
• Если последняя буква T или M равновероятно добавляется одна из {L, N}.
• Если последняя буква N или D равновероятно добавляется одна из {A, I}.
• Если последняя буква S или L равновероятно добавляется одна из {E, I, D}.
Петя очень азартный человек. Он ждёт, когда в строке появится подстрока ML.
Найдите математическое ожидание числа шагов (то есть приписанных букв), необ-
ходимых для того, чтобы это случилось.
Формат вывода
Ответ округлите до 2 знаков после запятой, используя в качестве разделителя точку.
Система оценивания
Точное совпадение ответа - 50 баллов.
Результаты тестирования не доступны во время проведения тура.
Страница 8 из 12
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Первый тур, 17 января 2026 года
G. Отрезки
Баллов за задачу:
50
Формат сдачи ответа:
ввод ответа
Количество попыток:
10
Посылка в зачет:
последняя
Условие
На общем сервере запланированы запуски обучения модели одинаковой длительно-
сти. Каждый запуск - отрезок времени. Концы всех отрезков различны. Админи-
стратор настроил очередь так, что в любой момент времени сервер занят максимум
двумя такими запусками.
Пусть A - число способов выбрать непустой набор запусков, которые можно прове-
сти без пересечений по времени.
Для примера:
• если на прямой расположен один отрезок, то A = 1;
• если расположены два пересекающихся отрезка, то A = 2;
• если расположены два непересекающихся отрезка, то A = 3.
Какие значение из отрезка [1500; 2025] может принимать величина A?
Формат вывода
В качестве ответа выведите все подходящие значения в порядке возрастания через
пробел.
Система оценивания
Точное совпадение ответа - 50 баллов.
Результаты тестирования не доступны во время проведения тура.
Страница 9 из 12
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Первый тур, 17 января 2026 года
H. Одинокий круг
Баллов за задачу:
50
Формат сдачи ответа:
программный код
Количество попыток:
100
Посылка в зачет:
Последняя
Ограничение времени:
10 секунд
Ограничение памяти:
64 Мб
Ввод:
стандартный ввод или input.txt
Вывод:
стандартный вывод или output.txt
Условие
Андрей готовится к собеседованию на стажировку по машинному обучению. Чтобы
разобраться с базовыми идеями классификации, он начал с самого простого случая:
если точки двух классов на плоскости можно разделить прямой, то метод опорных
векторов (SVM) строит разделяющую прямую
w1x + w2y + b = 0,
и знак выражения w1x + w2y + b определяет, к какому классу относится точка (с
одной стороны от прямой все точки будут иметь знак +, а с другой -).
Так Андрей познакомился с линейной классификацией.
Он нашёл простой пример кода, который показывает, как можно считать точки из
стандартного ввода, записать их в таблицу с колонками ‘x‘, ‘y‘, ‘label‘ и обучить по
этим данным линейный SVM:
import sys
import pandas as pd
from sklearn.svm import SVC
def read_points():
data
=
[]
tokens = sys.stdin.read().split()
it = iter(tokens)
n = int(next(it))
for _ in range(n):
x = float(next(it))
y = float(next(it))
label = int(next(it))
data.append((x, y, label))
df = pd.DataFrame(data, columns=["x", "y", "label"])
return df
df = read_points()
clf = SVC(kernel="linear")
clf.fit(df[["x", "y"]], df["label"])
Страница 10 из 12
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Первый тур, 17 января 2026 года
w1, w2
= clf.coef_[0]
b = clf.intercept_[0]
print(w1, w2, b)
Однако на собеседовании Андрею досталась другая задача.
Даны точки на плоскости с метками классов -1 и +1. Гарантируется, что существует
окружность с центром (x0, y0) и радиусом R > 0 такая, что
Формат вывода
Выведите три вещественных числа x, y и R - координаты и радиус разделяющей
окружности.
Система оценивания
Каждый пройденный тест даст вам 1 балл.
Максимальный возможный балл за задачу - 50.
Результаты тестирования доступны во время проведения тура.
Пример
Ввод
Вывод
10
003
0 0 -1
1 1 -1
2 0 -1
-2 0 -1
0 2 -1
401
-4 0 1
041
341
431
Примечания
Данная картинка соответствует первому примеру.
Оранжевые точки соответствуют label = -1, синие соответствуют label = 1.
Страница 11 из 12
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Первый тур, 17 января 2026 года
Страница 12 из 12
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Второй тур, 19 января 2026 года
A. Острова рекомендаций
Баллов за задачу:
100, по 20 за каждый вопрос
Формат сдачи ответа:
ввод или загрузка файла, в зависимости от вопроса
Количество попыток:
10 на каждую подзадачу
Посылка в зачет:
последняя
Условие
Вы работаете аналитиком в команде онлайн-маркетплейса. На сайте у каждого това-
ра есть карточка с информацией (категория, цена, рейтинг, бренд, наличие) и блок
«С этим также смотрят», где показаны другие товары, на которые пользователи
часто переходят из данной карточки.
Вам выдали выгрузку двух таблиц в формате CSV:
• items.csv - список товаров и их свойства.
• also_viewed.csv - список переходов «с этим также смотрят».
Нужно ответить на несколько вопросов про товары и структуру рекомендательного
графа. Ответы нужно получать с помощью программной обработки данных (напри-
мер, на Python с использованием pandas и простых алгоритмов работы с графами).
Файл items.csv содержит информацию о товарах. Каждая строка - один товар.
Поля:
• item_id - уникальный целочисленный идентификатор товара.
• category - категория товара (phones, accessories, laptops, books, home, toys).
• price - цена товара в условных единицах (целое число).
• rating - рейтинг товара по данным отзывов (вещественное число от 3.0 до 5.0
с шагом 0.1).
• brand - название бренда (строка).
• in_stock - 1, если товар есть в наличии, 0, если нет.
Файл also_viewed.csv описывает связи между товарами в блоке «с этим также
смотрят». Каждая строка задаёт пару товаров (item_from, item_to), для которых
зафиксировано, что пользователи часто переходят с одного на другой. В задачах, где
речь идёт о «соседях» товара или о переходах между товарами, будем считать, что
такая связь работает в обе стороны: если в таблице есть строка с парой товаров A и
B (в любом порядке), то A и B считаются напрямую связанными рекомендациями.
Для товара X его «соседями» считаются все товары, которые хотя бы в одной строке
стоят в паре с X - неважно, указан X в item_from или в item_to.
Система оценивания
За эту задачу можно получить до 100 баллов. Каждый пункт стоит 20 баллов.
Результаты тестирования подзадач 1, 2, 3 и 5 не доступны во время проведения
тура. Во всех подзадачах засчитывается последняя посылка.
Страница 1 из 10
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Второй тур, 19 января 2026 года
A1 - Вопрос 1
Сколько всего товаров категории phones имеют рейтинг не ниже 4.5 (rating ≥ 4.5) и
при этом есть в наличии (in_stock = 1)?
Формат вывода
Одно целое число - количество таких товаров.
Метрика оценивания точности ответа
Строгое совпадение введенного ответа.
A2 - Вопрос 2
Рассмотрим только товары категории laptops. Для каждого бренда посчитайте сред-
нюю цену ноутбуков этого бренда. Какой бренд имеет максимальную среднюю цену
среди ноутбуков?
Если несколько брендов имеют одинаковую максимальную среднюю цену, можно
вывести любой из них.
Формат вывода
Одно слово - название бренда (строка brand из файла items.csv).
Метрика оценивания точности ответа
Строгое совпадение введенного ответа.
A3 - Вопрос 3
Команда маркетинга хочет разделить товары на три сегмента по цене и рейтингу:
• сегмент premium - если rating ≥ 4.5 и price ≥ 50000;
• сегмент standard - если rating ≥ 4.0 и price < 50000;
• сегмент budget - во всех остальных случаях.
Для каждого товара определите его сегмент (premium / standard / budget) по
этим правилам. Среди товаров, которые есть в наличии (in_stock = 1), посчитайте,
сколько товаров относится к сегменту premium.
Формат вывода
Одно целое число - количество товаров сегмента premium среди товаров с in_stock =
1.
Метрика оценивания точности ответа
Строгое совпадение введенного ответа.
Страница 2 из 10
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Второй тур, 19 января 2026 года
A4 - Вопрос 4
Рассмотрим файл also_viewed.csv. Найдите все товары, которые хотя бы один раз
встречаются в поле item_to (то есть товары, которые хотя бы раз были показаны в
блоке «С этим также смотрят»). Для каждой категории посчитайте, сколько разных
товаров из этой категории встречается в item_to хотя бы один раз.
Нужно подготовить таблицу с двумя столбцами:
• category - название категории;
• cnt - количество разных товаров этой категории, которые встречаются в item_to.
В таблицу следует включить все категории, которые есть в файле items.csv, даже
если для какой-то категории cnt = 0. Строки в таблице нужно отсортировать по
названию категории в алфавитном порядке.
Формат вывода
Текстовый файл answer4.csv в формате CSV с заголовком и двумя колонками:
category,cnt
Файл должен содержать ровно по одной строке для каждой категории.
Метрика оценивания точности ответа
Доля категорий category в вашем файле-ответе для которых количество cnt совпа-
дает с количеством cnt в эталонном файле-ответе.
A5 - Вопрос 5
Будем рассматривать связи между товарами, как описано в разделе «Описание да-
тасета»: два товара считаются напрямую связанными, если в also_viewed.csv есть
строка, где они стоят парой (в любом порядке).
Назовём «островом рекомендаций» любое множество товаров, внутри которого из
любой карточки можно добраться до любой другой карточки, переходя по прямым
связям между товарами (по соседям). Если два товара относятся к разным островам
рекомендаций, то никакой цепочкой таких переходов из одного к другому попасть
нельзя.
Нас интересуют такие острова рекомендаций, в которых одновременно есть хотя бы
один товар категории phones и хотя бы один товар категории accessories.
Сколько таких островов рекомендаций существует в наших данных?
Формат вывода
Одно целое число - количество «островов рекомендаций», в которых есть и хотя бы
один phones, и хотя бы один accessories.
Метрика оценивания точности ответа
Строгое совпадение введенного ответа.
Страница 3 из 10
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Второй тур, 19 января 2026 года
B. Кластеризация
Баллов за задачу:
100
Формат сдачи ответа:
загрузка файла-ответа в формате .csv
Количество попыток:
20
Посылка в зачет:
последняя
Условие
По дороге на региональный этап ВсОШ ИИ Миша нашел флешку с брелком, на
котором написано “методкомиссия”. На флешке оказался табличный файл с назва-
нием data.csv. Поскольку целевой переменной в csv файле Миша не обнаружил,
он справедливо заключил, что это должна быть задача на кластеризацию. Однако,
информацию про количество кластеров Мише обнаружить нигде не удалось. Помоги
Мише понять количество кластеров и правильно кластеризовать данные.
Формат ввода
К задаче прикреплены файлы:
• data.csv - содержит матрицу объекты-признаки (каждая строка таблицы - объ-
ект, каждая колонка - признак). Колонка id - идентификатор объекта. Осталь-
ные колонки - признаки.
• baseline.ipynb - ноутбук с базовым решением задачи.
• submission.csv - пример решения, которое вам нужно отправить в тестирую-
щую систему.
Формат вывода
Вам нужно отправить как посылку файл submission.csv, содержащий две колонки:
• id - идентификатор объекта из data.csv.
• cluster - предсказанный вами кластер объекта (целое положительное число).
Система оценивания
За эту задачу можно получить до 100 баллов.
Данные разбиты на публичную и приватную части. Когда вы отправляете submission.csv,
вам показывается результат на публичной части. После завершения контеста ваш ре-
зультат будет пересчитан на приватной части.
После окончания этапа ваша метрика будет приведена к 100-балльной шкале по сле-
дующему правилу:
• результат baseline-решения (ARI=0.0) оценивается в 0 баллов;
• результат авторского решения (ARI=0.9814) оценивается в 100 баллов;
• результаты между этими точками распределяются линейно.
Метрика оценивания точности ответа
В этой задаче используется метрика ARI (Adjusted Rand Index). Чем большее
количество пар объектов Миша правильно распределяет по кластерам (например,
если оба объекта находятся в разных кластерах и Миша также разделяет их по
Страница 4 из 10
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Второй тур, 19 января 2026 года
разным кластерам, ИЛИ оба объекта находятся в одном кластере и Миша тоже их
определяет в один кластер), тем выше эта метрика. ARI принимает значение 0 для
случайного разбиения на кластеры, значение 1 для идеально правильного разбиения,
и может принимать отрицательные значения в случае неудачного разбиения хуже
случайного.
Пример расчета метрики ARI на P ython:
from sklearn.metrics import adjusted_rand_score
labels_true
=
[0,
0,
1,
1,
2,
2]
labels_pred
=
[1,
1,
0,
0,
2,
2]
ari = adjusted_rand_score(labels_true, labels_pred)
print("ARI
=", ari)
Страница 5 из 10
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Второй тур, 19 января 2026 года
C. Стоимость аренды квартир
Баллов за задачу:
100
Формат сдачи ответа:
загрузка файла-ответа в формате .csv
Количество попыток:
20
Посылка в зачет:
последняя
Условие
Пока Семён готовился к решению регионального этапа ВсОШ и мечтал, как полу-
чит свой БВИ, он решил прикинуть, какую квартиру он сможет снять на деньги,
накопленные на ML олимпиадах, если его не поселят в общежитие рядом с уни-
верситетом. Для этого он соскрэйпил данные с сайтов про аренду недвижимости и
решил построить модель предсказания стоимости аренды, чтобы затем найти самые
выгодные предложения. Однако, из-за того, что данные собирались не слишком ак-
куратно и с разных сайтов, датасет получился достаточно “грязным”. Помоги Семёну
аккуратно обработать данные и получить наилучшее качество прогноза стоимости
аренды.
Формат ввода
К задаче прикреплены файлы:
• train.csv - колонка price - целевая переменная. Остальные колонки - призна-
ки.
• test.csv - колонка id - идентификатор объекта. Остальные колонки - призна-
ки.
• baseline.ipynb - ноутбук с базовым решением задачи.
• submission.csv - пример решения, которое вам нужно отправить в тестирую-
щую систему.
Формат вывода
Вам нужно отправить как посылку файл submission.csv, содержащий две колонки:
• id - идентификатор объекта из test.csv.
• price - предсказанная вами целевая переменная.
Система оценивания
За эту задачу можно получить до 100 баллов.
Данные разбиты на публичную и приватную части. Когда вы отправляете submission.csv,
вам показывается результат на публичной части. После завершения контеста ваш ре-
зультат будет пересчитан на приватной части.
После окончания этапа ваша метрика будет приведена к 100-балльной шкале по сле-
дующему правилу:
• результат baseline-решения (RMSE=21.046) оценивается в 0 баллов;
• результат авторского решения (RMSE=13.8) оценивается в 100 баллов;
• результаты между этими точками распределяются линейно.
Страница 6 из 10
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Второй тур, 19 января 2026 года
Метрика оценивания точности ответа
В этой задаче используется метрика RMSE.
Строгое математическое определение метрики RMSE:
from sklearn.metrics import root_mean_squared_error
y_true
=
[3.0,
-0.5,
2.0,
7.0]
y_pred
=
[2.5,
0.0,
2.1,
7.8]
rmse = root_mean_squared_error(y_true, y_pred)
print("RMSE
=", rmse)
Страница 7 из 10
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Второй тур, 19 января 2026 года
D. Марсианский Архивариус
Баллов за задачу:
100
Формат сдачи ответа:
загрузка файла-ответа в формате .csv
Количество попыток:
20
Посылка в зачет:
последняя
Условие
Во время третьего года экспедиции в долине Маринер на Марсе наш знакомый инже-
нер Андрей - специалист по внеземным системам - наткнулся на нечто невероятное:
идеально сохранившийся кристаллический модуль памяти, скрытый глубоко под по-
верхностью каньона.
Когда модуль осторожно извлекли и подключили к питанию, он пробудил древний
марсианский ИИ, назвавший себя «Архивариус блок F» - хранителем знаний исчез-
нувшей цивилизации. Архивариус поведал, что в его памяти содержатся обширные
сведения о кристаллах, которые древние марсиане использовали в своих лаборато-
риях и энергетических реакторах.
Каждый кристалл был описан эмбеддингом - вектором длины 16, отражающим его
структуру, состав, резонансные свойства и ещё множество характеристик, которые
люди пока не умеют интерпретировать напрямую. Помимо эмбеддинга, Архивариус
хранил и класс (из 25 возможных) - тип или функциональное назначение кристалла.
Но за тысячи лет под марсианской пылью Архивариус был повреждён.
Для многих кристаллов информация о классе оказалась полностью утеряна.
Для других сохранилась только частично: вместо одного точного класса Архивариус
выдавал несколько возможных вариантов, иногда разумных, а иногда - совершенно
случайных. Похоже, что структуры данных внутри модуля перемешались, и никакой
простой метод восстановления информации не работает.
Андрея и его команду чрезвычайно интересуют древние знания о марсианских кри-
сталлах - понимание их свойств может стать прорывом в энергоёмких технологиях
и материаловедении.
Вот почему они обращаются к вам.
Ваша задача - помочь Архивариусу восстановить истинные классы тех кристаллов,
для которых информация была утеряна или повреждена. Вам будут предоставлены:
• эмбеддинги кристаллов,
• корректные классы для части из них,
• неоднозначные списки возможных классов для остальных,
• а также набор кристаллов, чьи классы предстоит предсказать.
Как и древний ИИ, вам придётся работать в условиях неопределённости и неполной
информации. Однако современные методы машинного обучения дают шанс восста-
новить значительную часть утраченных знаний - если применить их достаточно
аккуратно и изобретательно.
Страница 8 из 10
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Второй тур, 19 января 2026 года
Формат ввода
К задаче прикреплены файлы:
• train.csv - содержит информацию о кристаллах, для которых известны ис-
тинные или возможные метки. Поля:
- id - уникальный идентификатор объекта.
- F{i}, где i ∈ {1, . . . , 16} - компоненты эмбеддинга.
- labels - набор возможных классов для данного объекта (истинный класс
может присутствовать среди них, но может и отсутствовать).
• test.csv - файл с эмбеддингами кристаллов, чьи классы необходимо пред-
сказать. Гарантируется, что каждый объект относится ровно к одному из 25
классов.
• baseline.ipynb - ноутбук с базовым решением задачи.
• submission.csv - пример решения, которое вам нужно отправить в тестиру-
ющую систему.
Формат вывода
Вам нужно отправить как посылку файл submission.csv, содержащий две колонки:
• id - идентификатор объекта из test.csv.
• class - предсказанная моделью метка класса
Система оценивания
Максимум за задачу - 100 баллов.
Данные тестовой выборки разделены на публичную и приватную части.
После отправки решения система показывает результат на публичной части.
Окончательный результат после завершения контеста будет рассчитан по приватной
части. После окончания этапа ваша метрика будет приведена к 100-балльной шкале
по следующему правилу:
• результат baseline-решения (Accuracy=0.2717) оценивается в 0 баллов;
• результат авторского решения (Accuracy=0.8) оценивается в 100 баллов;
• результаты между этими точками распределяются линейно.
Метрика оценивания точности ответа
В этой задаче используется метрика Accuracy. Она считается как доля объектов
тестовой выборки, для которых класс предсказан верно.
Строгое математическое определение метрики Accuracy:
число верных ответов
Accuracy =
общее число тестовых кристаллов
Страница 9 из 10
Всероссийская олимпиада школьников по информатике 2025-2026, Региональный этап, 9-11 класс
Профиль «Искусственный интеллект», Второй тур, 19 января 2026 года
Пример расчета метрики Accuracy на P ython:
from sklearn.metrics import accuracy_score
y_true
=
[0,
1,
2,
2,
1]
y_pred
=
[0,
2,
1,
2,
1]
acc = accuracy_score(y_true, y_pred)
print("Accuracy
=", acc)
Страница 10 из 10
Разбор заданий муниципального этапа ВсОШ 2025/26 по искусственному интеллекту...7-8 классы
Максимальное количество баллов за олимпиаду - 600
Задание 1. Задача по геометрии
Саша нарисовал выпуклый многоугольник с помощью компьютерной программы и отправил модели ИИ запрос вы-
числить сумму его углов. Он получил неверный ответ 500. Оказалось, что один из углов многоугольника модель
учла дважды. Чему равен этот угол? Ответ выразите в градусах. Напомним, что в выпуклом многоугольнике все
углы меньше 180.
Ответ: 140
Критерий оценивания: точное совпадение ответа - 100 баллов
Максимальный балл за задание - 100
Решение. Если в Сашином многоугольнике хотя бы пять вершин, то сумма его углов хотя бы 180 · 3 = 540, этот
случай невозможен. Если это треугольник, сумма углов равна 180, и учтенный дважды угол должен быть больше
180, что невозможно. Значит, у Саши четырехугольник, его сумма углов равна 360, и дважды был посчитан угол
величиной 500 - 360 = 140.
Задание 2. Математика в чат-боте
Дима выбрал два натуральных числа a и b и затем отправил запрос модели ИИ найти значение выражения a + b ·2 (он
записал это выражение на бумажке, сфотографировал и загрузил полученную фотографию). Из-за неаккуратного
почерка модель распознала записанное выражение как a + b2 и в результате ответ модели оказался на 80 больше
правильного. Найдите последнюю цифру числа a · b.
Ответ: 0
Критерий оценивания: точное совпадение ответа - 100 баллов
Максимальный балл за задание - 100
Решение. Исходя из условия, мы получаем, что b2 - 2b = 80, то есть (b - 10)(b + 8) = 0. Следовательно, b = 10,
поскольку число b - натуральное, а тогда ab оканчивается нулём.
Задание 3. Градиентный спуск
На прямой изучают работу очень простого «искусственного интеллекта», который зависит всего от одного числа -
параметра w. Для каждого целого w от 1 до 10 заранее посчитана ошибка E(w) этого ИИ на обучающих примерах:
w
1
2
3
4
5
6
7
8
9
10
E(w)
9
5
2
4
6
7
3
1
2
4
ИИ обучают с помощью следующего алгоритма изменения параметра w (аналог градиентного спуска).
1. Сначала выбирают начальное целое значение параметра w от 1 до 10.
2. Рассматривают «соседей» текущего значения w:
• слева - число w - 1 (если w > 1);
• справа - число w + 1 (если w < 10).
3. Если среди существующих соседей есть такие, у которых ошибка строго меньше: E(wсосед) < E(w), то переходят
к тому соседу, у которого ошибка наименьшая (среди соседей).
4. Затем снова выполняют шаг 2 и так далее, пока не окажется, что у всех существующих соседей ошибка не меньше
текущей. B этот момент алгоритм останавливается; говорят, что он застрял в локальном минимуме.
Из таблицы видно, что наименьшее значение ошибки достигается при w = 8; это глобальный минимум.
В реальных задачах часто запускают обучение много раз из разных начальных точек, чтобы увеличить шанс
попасть в глобальный минимум. Будем считать, что:
• в каждом запуске начальное значение w выбирается случайно и равновероятно из чисел 1, 2, ..., 10;
• разные запуски независимы;
• запуск считается успешным, если алгоритм в итоге остановился в глобальном минимуме при w = 8.
При каком наименьшем количестве запусков вероятность того, что хотя бы один запуск окажется успешным, будет
не меньше 95%?
Ответ: 5
Критерий оценивания: точное совпадение ответа - 100 баллов
Максимальный балл за задание - 100
Решение. По таблице видно, что слева все траектории спуска (из w = 1, 2, 3, 4, 5) приходят в локальный минимум
при w = 3, а справа все траектории (из w = 6, 7, 8, 9, 10) приходят в глобальный минимум при w = 8.
1
Задание 4. Лидер продаж в категории
Интернет-магазин агрегирует товары по категориям. Данные находятся в файле, который вы можете скачать в фор-
матах XLSX, ODS или CSV.
Для каждого товара известны три значения: category, score, label.
• category - категория товара;
• score - оценка интереса, число из диапазона [0, 1];
• label - факт покупки: 0 или 1.
В каждой категории на витрине показываются все товары, у которых значение score является максимальным
среди всех товаров той же категории. Для каждой категории найдите её максимальную оценку:
max_score(g) = max{score : category = g}.
Выберите все строки, где score = max_score(g). Если в категории несколько товаров делят максимум, выбираются
все. Сколько выбранных строк имеют label = 1?
Ответ: 49
Критерий оценивания: точное совпадение ответа - 100 баллов
Максимальный балл за задание - 100
Решение. В каждой категории g находим максимальную оценку интереса
max_score(g) = max{score : category = g},
и выбираем все строки, где score = max_score(g). Ответ - сумма значений label среди выбранных строк.
Код (pandas):
import pandas as pd
df = pd.read_csv("task9_group_top_select.csv")
max_score = df . groupby (" category " ) [ " score " ] . transform ("max")
sel = df["score"] == max_score
answer = df . loc [ sel , " label " ] .sum()
print(answer)
Задание 5. ICPC
Ограничение по времени: 2 секунды
Ограничение по памяти: 256 мегабайт
В машинном обучении ансамбли работают лучше, когда в них есть разнообразие моделей: смешивают разные
архитектуры и источники признаков, чтобы усилить общий результат. Однородные ансамбли часто переобучаются
и хуже обобщают, а разнородные - устойчивее и сильнее.
Рассмотрим команды как ансамбль людей. Скоро начнётся новый сезон ICPC, а значит, известному тренеру Миха-
илу необходимо собрать команду, которая его выиграет! Ранее такое уже случалось, получится и в этом году. Секрет
прост - нужно, чтобы в команде были и математики, и программисты. Если в команду войдут только программисты
2
Разбор заданий муниципального этапа ВсОШ 2025/26 по искусственному интеллекту...7-8 классы
или только математики, результат хорошим не будет. Прямо сейчас у Михаила для распределения есть n математиков
и m программистов.
Сколькими способами можно собрать ровно одну команду из трёх человек на ICPC?
Формат входных данных
Первая строка содержит два целых числа n и m (1 n, m 105) - количества математиков и информатиков
соответственно.
Формат выходных данных
Выведите одно целое число - количество способов собрать одну успешную команду из трёх человек для участия
в ICPC.
Примеры
стандартный ввод
стандартный вывод
2
9
3
Критерий оценивания: точное совпадение ответа - 100 баллов
Максимальный балл за задание - 100
Решение
Есть два непересекающихся способа собрать команду: два математика + программист и два программиста + матема-
тик.
Посчитаем общую формулу: сколько есть способов собрать команду, если двух людей нужно выбрать из группы из
a человек, а одного человека нужно выбрать из группы из b человек. Двух людей можно выбрать a(a -1)/2 способами,
одного человека - b способами. Для каждого выбора пары подходит любой выбор одиночного участника, значит всего
способов a(a - 1)/2 · b.
Применяя это к нашей задаче, получаем итоговый ответ: n(n - 1)/2 · m + m(m - 1)/2 · n.
#include <iostream >
#include <c s t d i n t >
using namespace std ;
int main()
{
int64_t n, m;
cin >> n >> m;
cout << m ∗ n ∗ (n - 1)
/ 2 + n ∗ m ∗ (m - 1)
/
2;
return
0;
}
Задание 6. Коллектив
Ограничение по времени: 2 секунды
Ограничение по памяти: 256 мегабайт
В эпоху больших данных обучение становится распределённым, а ресурсы - на вес золота. B случае распределённо-
го обучения на разных устройствах какие-то операции возможно производить эффективно, только если необходимые
модули находятся в одном кластере. B таких случаях очень важно уметь распределять вычисления для достижения
наилучшего результата с данными ресурсами.
Эту идею можно продемонстрировать на взаимодействии людей. В исследовательском центре одной небезызвестной
компании работает n сотрудников. Про каждого сотрудника известно, что он является выпускником вуза с номером
bi, а его личный вклад в решение сложных задач равен ai.
Сотрудникам необходимо решить очень сложную задачу. Для большей эффективности они будут работать над за-
дачей парами. Рассмотрим все пары различных сотрудников (i,j), где 1 i < j n:
• пара сотрудников (i,j) считается допустимой, если они выпускники одного и того же вуза, то есть bi = bj;
• вклад допустимой пары (i,j) в решение равен ai + aj;
• если bi ≠ bj, то такая пара не является допустимой и не даёт вклада в решение.
3
Разбор заданий муниципального этапа ВсОШ 2025/26 по искусственному интеллекту...7-8 классы
Каждый работник может входить в несколько допустимых пар с разными коллегами. При этом каждая конкретная
пара сотрудников (i,j) учитывается не более одного раза. Требуется посчитать суммарный вклад всех допустимых пар
сотрудников.
Формат входных данных
Формат выходных данных
Выведите одно целое число - суммарный вклад всех допустимых пар сотрудников.
Замечание
В первом примере есть только один выпускник вуза 1, поэтому он не сможет образовать ни одной пары. При этом
есть 3 выпускника вуза 2 и они образуют пары, которые дадут вклады 4 + 8, 4 + 8, 4 + 4 в решение. Суммарный вклад
будет 12 + 12 + 8 = 32.
Примеры
стандартный ввод
стандартный вывод
4
32
4814
2212
Критерий оценивания: точное совпадение ответа - 100 баллов
Максимальный балл за задание - 100
Решение
В итоговый результат вклад дают только пары с одинаковым b. Давайте для каждого значения b посчитаем cnt[b] -
количество людей с таким b.
Заметим, что каждый человек i образует допустимую пару ровно с cnt[bi] - 1 людьми. В каждую такую пару он
даёт вклад ai (вторую половину вклада даёт другой человек), поэтому суммарный вклад человека i в ответ равен
(cnt[bi] - 1) · ai. Ответ получается суммированием этой величины по всем людям.
4
Разбор заданий муниципального этапа ВсОШ 2025/26 по искусственному интеллекту...7-8 классы
#include <b i t s / s t d c++.h>
using namespace std ;
int main()
{
ios ::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n ;
vector<long long> a(n) , b(n);
for (int i = 0; i < n; ++i ) cin >> a [ i ] ;
for (int i = 0; i < n; ++i ) cin >> b[ i ] ;
const int MAXB = 1000000;
vector<long long> cnt (MAXB + 1 ,
0);
for (int i = 0; i < n; ++i ) ++cnt [b[ i ] ] ;
long long ans = 0;
for (int i = 0; i < n; ++i )
{
ans += a [ i ]
∗ (cnt[b[i ]] - 1);
}
cout << ans <<
’\n’;
return
0;
}
5
Разбор заданий муниципального этапа ВсОШ 2025/26 по искусственному интеллекту 9-11 классы
Максимальное количество баллов за олимпиаду - 600
Задание 1. Математика в чат-боте
Дима выбрал два натуральных числа a и b. Затем он отправил модели ИИ запрос - вычислить ab. Он записал это
выражение на бумажке, сфотографировал и загрузил фотографию. Из-за неаккуратного почерка модель распознала
выражение как a · b и посчитала именно его. В итоге её ответ оказался меньше правильного на 110.
Какой ответ выдала модель?
Ответ: 15
Задание 2. Максимальный след по всем перестановкам
Матрицей n ×m будем называть таблицу из чисел, состоящую из n строк и m столбцов. Умножение матриц выполняют
по правилу «строка на столбец». Если
а) Hайдите наибольшее возможное значение tr(AB).
Ответ: 339
Критерий оценивания: точное совпадение ответа - 50 баллов
б) Hайдите наибольшее возможное значение tr(BA).
Ответ: 339
Критерий оценивания: точное совпадение ответа - 50 баллов
Максимальный балл за задание - 100
Задание 4. Group By
Cистема электронного тестирования фиксирует результаты проверочных работ школьников, которые вы можете ска-
чать в форматах XLSX, ODS или CSV. Файл содержит пять столбцов:
• student_id - идентификатор ученика (целое число);
• subject - предмет, по которому выполнен тест (строка);
• score - полученный балл (вещественное число);
• cheat_flag - подозрение на списывание (True/False);
• attempt_no - номер попытки (целое число, 1 означает первую попытку).
Выполните следующие действия с данными:
1. Очистите столбец score: пустые значения замените на 0, значения меньше 0 также замените на 0, значения
больше 100 замените на 100.
2. Удалите строки, где cheat_flag = True.
3. Оставьте только строки, соответствующие первой попытке, то есть те, где attempt_no = 1.
4. Для каждого ученика вычислите его средний балл по предметам - это среднее всех значений score, которые
остались после действий выше.
2
Разбор заданий муниципального этапа ВсОШ 2025/26 по искусственному интеллекту 9-11 классы
Сколько учеников имеют средний балл в диапазоне 60 avg_score < 80?
Ответ: 363
Критерий оценивания: точное совпадение ответа - 100 баллов
Максимальный балл за задание - 100
Решение. Решение задачи на языке Python:
import pandas as pd
df = pd.read_csv("tests.csv")
df["score"] = df["score"]. fillna(0)
mask_neg = df [ " score " ] < 0
df.loc[mask_neg, "score"] = 0
mask_high = df [ " score " ] > 100
df.loc[mask_high, "score"] = 100
df = df[df["cheat_flag"] == F a l s e ]
df = df[df["attempt_no"] == 1 ]
mean_by_student = df . groupby ("student_id" ) [ " score " ] . mean()
cond = (mean_by_student >= 60) & (mean_by_student < 80)
answer = cond .sum()
print(answer)
Задание 5. Дерево непринятия решений
Ограничение по времени: 1 секунда
Ограничение по памяти: 256 мегабайт
В машинном обучении часто используют деревья решений. Каждая внутренняя вершина такого дерева соответствует
некоторому вопросу, а каждое ребро - выбору ответа (да/нет). Таким образом, за несколько вопросов исходные дан-
ные могут быть разбиты на достаточно большое количество классов. Очередным проектом для Димы стало дерево
непринятия решений. Его структура похожа на структуру решающего дерева. Это полное бинарное дерево глуби-
ны n. В каждой вершине, кроме вершин последнего уровня, хранится число p (0 p 100). Это число обозначает
вероятность выбора: с вероятностью p процентов алгоритм выберет пойти влево и, соответственно, с вероятностью
100 - p процентов - вправо.
Договоримся: движение влево обозначим цифрой 0, а движение вправо - цифрой 1. Таким образом, каждая вершина
нижнего уровня соответствует двоичной строке длины n (последовательности решений от корня до листа).
Вероятность получения этой строки равна произведению вероятностей всех выборов, сделанных на пути от корня
до листа.
Дима уже написал структуру для такого дерева и хочет протестировать её. Для этого он создал дерево глубины 3.
Значит, всего в нём 7 внутренних вершин. Каждая вершина имеет своё число p. Ниже показана схема расположения
этих вершин:
3
Разбор заданий муниципального этапа ВсОШ 2025/26 по искусственному интеллекту 9-11 классы
Найдите вероятности всех двоичных строк длины 3 и выведите их в порядке неубывания вероятности. Если веро-
ятности совпадают, строки должны выводиться в лексикографическом порядке.
Формат входных данных
В первой строке заданы 7 целых чисел p (0 p 100) - вероятности для вершин, как показано на рисунке.
Формат выходных данных
Выведите 8 строк. Каждая строка должна содержать двоичную строку длины 3. Строки должны идти в порядке
неубывания вероятности. При равенстве вероятностей строки сравниваются лексикографически.
Примеры
стандартный ввод
стандартный вывод
40 90 20 90 100 70 0
011
110
001
101
010
100
000
111
Замечание
В первом тестовом примере двоичные строки имеют следующие вероятности:
• 011 - 0.0
• 110 - 0.0
• 001 - 0.036
• 101 - 0.036
• 010 - 0.04
• 100 - 0.084
• 000 - 0.324
• 111 - 0.48
Решение
В задаче нужно посчитать вероятность для каждого листа. Так как каждая такая вероятность является произведением
трёх чисел (вероятностей выбора в вершинах), для сравнения достаточно сравнивать произведения этих чисел, умно-
женных на 100 (то есть вероятности в процентах). Давайте явно выпишем все 8 таких произведений и для каждого
запомним соответствующую строку из трёх бит. После этого отсортируем пары (вероятность, строка) по вероятности
и выведем строки в получившемся порядке. Это и будет ответом.
#include <iostream >
#include <vector >
#include <algorithm >
using namespace std ;
int main()
{
ios ::sync_with_stdio(false);
cin.tie(nullptr);
int p1, p2, p3, p4, p5, p6, p7;
cin >> p1 >> p2 >> p3 >> p4 >> p5 >> p6 >> p7 ;
int p000 = p1 ∗ p2 ∗ p4;
int p001 = p1 ∗ p2 ∗
(100 - p4);
int p010 = p1 ∗
(100 - p2) ∗ p5;
int p011 = p1 ∗
(100 - p2)
(100 - p5);
int p100 = (100 - p1) ∗ p3 ∗ p6;
4
Разбор заданий муниципального этапа ВсОШ 2025/26 по искусственному интеллекту 9-11 классы
int p101 = (100 - p1) ∗ p3 ∗
(100 - p6);
int p110 = (100 - p1)
(100 - p3) ∗ p7;
int p111 = (100 - p1)
(100 - p3)
(100 - p7);
vector<pair<int , string>> v = {
{p000 , "000"} ,
{p001 , "001"} ,
{p010 , "010"} ,
{p011 , "011"} ,
{p100 , "100"} ,
{p101 , "101"} ,
{p110 , "110"} ,
{p111 , "111"}
};
sort(v.begin(), v.end());
for (auto &[p, s] : v)
{
cout << s <<
’\n’;
}
return
0;
}
Максимальный балл за задание - 100
Задание 6. Тепловая карта
Ограничение по времени: 1 секунда
Ограничение по памяти: 256 мегабайт
Слава готовит постер на конференцию. К сожалению, сейчас его тепловая карта не влезает
на постер: она слишком большая. Поэтому Слава решил выделить на текущей карте неко-
торый прямоугольный фрагмент и использовать его для презентации. Славина тепловая
карта выглядит как таблица из n строк и m столбцов. Клетка на пересечении i-й строки
и j-го столбца имеет цвет cij. Используются k цветов, которые пронумерованы от 1 до k.
Пример аналогичной карты приведён справа. Слава хочет продемонстрировать весь раз-
мах значений, поэтому на выбранном фрагменте должна быть хотя бы одна клетка каждого
цвета. При этом юный докладчик хочет минимизировать площадь карты, ведь ему нужно
уместить её на постер.
Помогите ему: найдите прямоугольник, который можно будет вырезать из его карты так,
чтобы на нём были клетки всех k цветов. Гарантируется, что на исходной тепловой карте
присутствуют клетки всех k цветов.
Формат входных данных
Формат выходных данных
Выведите 4 числа x1, y1, x2, y2, задающие прямоугольник, который нужно вырезать. Прямоугольник задаётся своими
верхней и нижней строками (x1,x2) и левым и правым столбцами (y1,y2). Если есть несколько способов вырезать
график наименьшей площади, выведите любой.
Примеры
стандартный ввод
стандартный вывод
343
1324
1122
3113
1222
5
Разбор заданий муниципального этапа ВсОШ 2025/26 по искусственному интеллекту 9-11 классы
Замечание
Все возможные варианты вырезать график наименьшей площади для первого примера.
Решение
#include <iostream >
#include <vector >
#include <algorithm >
using namespace std ;
int main()
{
ios ::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, k;
cin >> n >> m >> k ;
vector<vector<int>> v(n, vector<int>(m));
for (int i = 0; i < n; ++i )
{
for (int j = 0; j < m; ++j )
{
cin >> v [ i ] [ j ] ;
−-v [ i ] [ j ] ;
}
}
vector<vector<vector<int>>> pref (
k , vector<vector<int>>(n + 1 , vector<int>(m + 1 ,
0))
);
for (int c = 0; c < k; ++c)
{
for (int i = 1; i <= n; ++i )
{
6
Разбор заданий муниципального этапа ВсОШ 2025/26 по искусственному интеллекту 9-11 классы
for (int j = 1; j <= m; ++j )
{
pref[c][ i ][ j] = pref[c][ i - 1][j] + pref[c][ i ][ j - 1]
- pref[c][ i - 1][j- 1]
+ (v[i - 1][j
- 1] == c );
}
}
}
int min_area = n ∗ m;
vector<int> ans (4 ,
-1);
for (int l = 0; l < n; ++l )
{
for (int r = l; r < n; ++r )
{
int
y = 0;
for
(int x = 0; x < m; ++x ) {
if (x > y) y = x;
while (y < m)
{
bool ok = true ;
for (int c = 0; c < k;
++c ) {
int cnt = pref[c][r + 1][y + 1]
- pref[c][ l ][y + 1]
− pref[c][r + 1][x]
+ pref[c][ l ][x];
if (cnt == 0) {
ok = false ;
break ;
}
}
if (ok) break;
++y ;
}
if (y == m) {
x = y;
continue ;
}
int area = (r - l + 1)
∗ (y - x + 1);
if (area < min_area)
{
min_area = area ;
ans = {l + 1, x + 1, r + 1, y + 1};
}
}
}
}
cout << ans [0] << ’ ␣ ’ << ans [ 1 ] <<
’␣’ << ans [ 2 ] << ’ ␣ ’ << ans [ 3 ] <<
’\n’;
return
0;
}
Максимальный балл за задание - 100
7
A. Прямая снова крутится
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Первый тур. Москва, 23 марта 2026 г.
B. Генератор случайности
Решение.
Ответ: m = 10.
Страница 2 из 11
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Первый тур. Москва, 23 марта 2026 г.
C. Градиентный спуск на листочке
Рассмотрим функцию f(x, y) = x20 +y26. Ниже приведён алгоритм, который по начальной
точке (a, b) строит последовательность точек (Xi, Yi), i = 0, 1, 2, . . .
Ниже в коде decrease_lr = False для пункта (a), и decrease_lr = True для пункта (b).
X0,Y0 ← a,b
dx, dy ← 0, 1
lr ← 1
for i = 1, 2, 3, . . . do
dx, dy ← dy, -dx
if decrease_lr and i · lr ⩾ 2 then:
lr ← lr / 2
end if
Xi,Yi ← Xi-1,Yi-1
if f(Xi + dx · lr, Yi + dy · lr) < f(Xi, Yi) then
Xi ← Xi + dx · lr
Yi ← Yi + dy · lr
end if
end for
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Первый тур. Москва, 23 марта 2026 г.
Тогда и расстояние до начала координат в этого момент будет меньше, чем 1:
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Первый тур. Москва, 23 марта 2026 г.
D. Лазер
Пол прямоугольной камеры с зеркальными стенами имеет форму клетчатого прямоуголь-
ника 1000 × 3000. В узлах сетки расположены датчики температуры (всего 1001 · 3001
датчиков). Изначально все датчики показывают температуру 0C.
В центрах некоторых клеток расположено по одному устройству, испускающему ла-
зерные лучи. Каждое устройство испускает лазерный луч по диагонали клетки, в центре
которой оно находится (то есть в одном из четырех возможных направлений, в сторону
одного из узлов соответствующей клетки). При этом то же устройство поглощает лучи,
приходящие из противоположного узла. На лучи трёх других направлений устройство не
влияет.
Всего установлено 300 устройств, которые излучают по одному лучу. Каждый луч отра-
жается зеркально от стенок (попадая в угол, луч отражается в противоположном направ-
лении), пока не поглотится каким-то из устройств.
Лазерный луч, «посещая» очередной датчик температуры, увеличивает его показание на
1C. Если же в точке, где располагается датчик, луч отражается, он увеличивает показание
соответствующего датчика сразу на 2C. Назовём тепловой картой матрицу A размера
1001 × 3001, элементы которой равны итоговым показаниям соответствующих датчиков
температуры.
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Первый тур. Москва, 23 марта 2026 г.
Покажем, что оно подходит. Заметим, что траектории всех лучей разбиваются на циклы
потому что каждый датчик принимает и испускает ровно один луч (отметим, что один
цикл может быть объединением траекторий сразу нескольких лучей). Заметим, что доста-
точно проверить условие для одной циклической траектории (в силу линейности свёртки).
Будем мыслить такую траекторию как набор диагональных отрезков между последова-
тельными отражениями, где каждый отрезок увеличивает значения каждого датчика, ко-
торый на нём расположен, на 1C. Легко видеть, что тепловая карта траектории равна
сумме тепловых карт этих отрезков.
Значит, достаточно проверить, что в результате свёртки с ядром K любой матрицы, в ко-
торой единицы расположены на одном диагональном ряду (остальные элементы - нули),
получается нулевая матрица. Это следует из того, что сумма элементов матрицы K на
любом диагональном ряду равна нулю.
Остаётся доказать, что меньшие значения s не подходят. Достаточно доказать это для
s = 2. Предположим обратное, что есть определяющее ядро
Страница 6 из 11
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Первый тур. Москва, 23 марта 2026 г.
E. Новая выборка
Решение.
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Первый тур. Москва, 23 марта 2026 г.
Доказательство. Точка (a, b) выбирается методом наименьших квадратов, то есть совпа-
дает с наименьшем значении функции
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Первый тур. Москва, 23 марта 2026 г.
F. Вымышленная ситуация
Артур и Таня готовят вычислительные мощности к практическому туру для 1000 участ-
ников финала всероссийской олимпиады школьников по ИИ. Изначально они хотели раз-
вернуть всю инфраструктуру в облаке и выдавать видеокарты (GPU) из общего набора,
но, чтобы исключить сетевые задержки, было решено собрать каждому участнику персо-
нальный кластер из 8 устройств.
На складе партнёров олимпиады есть по 8000 устройств каждого из трех типов.
• Тип 1 (зеленые) - CUDA, быстрые, но горячие.
• Тип 2 (красные) - поддерживают открытые стандарты, подходят для обучения боль-
ших языковых моделей (LLM), но требуют сложной настройки драйверов.
• Тип 3 (желтые) - экспериментальные ускорители (TPU).
По условиям поставки устройства типа 1 можно получать только партиями по 7 штук;
устройств каждого типа должно быть не меньше, чем 2026; всего со склада следует взять
ровно 8000 устройств. Если в соответствии с этими условиями можно привезти со склада
y1 устройств первого типа, y2 - второго и y3 - третьего, будем называть тройку целых
чисел (y1, y2, y3) допустимой, то есть:
Решение.
Будем называть величины ui,j полезностями устройств типа j для участника i.
Страница 9 из 11
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Первый тур. Москва, 23 марта 2026 г.
Лемма 1. Если умножить значения полезностей произвольного участника i на одно и
то же положительное число, любое честное распределение останется честным, а любое
нечестное - нечестным.
Доказательство. Обе части неравенств, в которых фигурирует полезность Vi, мы умно-
жим на одну и ту же положительную константу, тем самым выполнение системы новых
и изначальных неравенств равносильно.
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Первый тур. Москва, 23 марта 2026 г.
Пункт (a).
Страница 11 из 11
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Критерии проверки первого тура.
A. Прямая снова крутится
A. Верный ответ без проверки - 45 баллов.
B. Генератор случайности
A. Найдено P(6) = (m - 5)/m - 5 баллов
B. Найдена вероятность последовательности из условия при фиксированном m - 15 баллов.
C. Заявлено, что указанная в предыдущем критерии вероятность возрастает при целых m от 6
до 10 и убывает далее - 15 баллов
D. Вероятность посчитана с ошибкой в константу раз (и другие не влияющие на ход решения
неточности) - снимается не менее 5 баллов.
M. Доказано, что число 10 является экстремумом. Используется, но не доказано, что это именно
максимум - снимается не менее 5 баллов.
• Все приведенные продвижения и штрафы суммируются.
C. Градиентный спуск на листочке
Общая часть.
C1 (5 баллов) Сформулировано чередование направлений спуска с периодом 4.
C2 (5 баллов) Установлено убывание |X| и |Y |.
Пункт (а).
A. Верное доказательство пункта (а) - 20 баллов.
MA. Неверно указана область остановки или используется без доказательства существование
такой области - снимается 10 баллов.
Пункт (b).
B. Верное доказательство пункта (b) - 20 баллов.
B1. Введена структура блоков, с указанием связи их размера и общим значением lr - 5 баллов.
B2. Доказано, что вклад каждого блока в изменение параметров |X| и |Y | составляет не мень-
ше некоторой константы, если все шаги этого блока приводили к изменению соответствующих
координат.
MB. Ошибки в изложении оставшейся части доказательства - снимается не менее 5 баллов.
• Продвижения и штрафы по трем приведенным частям задачи суммируются.
D. Лазер
Часть А (пример). Часть B (оценка).
А1. Приведена правильная матрица K - 10 баллов.
А2. Показано, что матрица обнуляет диагональные ряд - 5 баллов.
А3. Завершение обоснования, почему матрица K является определяющим ядром - 10 баллов.
B1. Получено линейное уравнение на элементы ядра - 5 баллов.
М. Ошибки в построении «подходящияго луча» (он рассматривается только локально, ставится
меньшее число устройств, чем требует условие и т.д.) - снимается не менее 5 баллов.
• Все приведенные продвижения и штрафы суммируются.
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Критерии проверки первого тура.
E. Новая выборка
Общеизвестные свойства линейной регрессии принимаются без доказательства при наличии
верной формулировки.
A. Верный ответ - 10 баллов.
B. Доказано, что f = g - 10 баллов.
• Если только сформулировано, что f = g, выставляется 5 баллов.
• Если доказательство опирается на свойства линейной регрессии, должно быть явно сформули-
ровано свойство добавляемых данных, которое позволяет сделать вывод о равенстве коэффици-
ентов моделей.
• Если это используется и даже не формулируется, баллы по критерию B не начисляются.
• За вывод о равенстве числителей в выражениях коэффициента детерминации дополнительные
баллы не начисляются.
C1. Установлены зависимости между yi, xi, f(xi) (лемма 2) - 10 баллов.
C2. Получены выражения для a и b (формула коэффициентов линейной регрессии) с доказатель-
ством равенства средних - 10 баллов.
• Должны быть установлены обе зависимости (в том виде, как это указано С1 или С2) и явно
сформулировано равенство средних значений старой и новой выборки. За получения любого
одного из соотношений начисляется 5 баллов за часть C.
M. Лемма 3 (или аналогичное равенство) используется в решении без доказательства - снимается
15 баллов.
• За алгебраические преобразования, не приводящие к зависимости R21 только от R20 баллы не
начисляются.
F. Вымышленная ситуация
A. Верный ответ в пункте (a) - 0 баллов.
B. Лемма 1 (о домножении полезностей на константу) вместе с наблюдением о разделении участ-
ников на две группы - 10 баллов.
B1. Указано разделение агентов делятся на 2 группы по предпочтениям - 5 балло.
B2. Лемма 1 только сформлуирована - 5 баллов.
C. Формулировка и доказательство леммы 2 и 3 (о равенстве кластеров внутри каждой из групп
участников) - 15 баллов.
C1. Формулировка леммы 2 и 3 - 5 баллов.
D. Вывод пункта (а) из лемм 2 и 3 - 5 баллов.
E. Ответ в пункте (б) - 10 баллов.
F. Проверка ответа в пункте (б) - 10 баллов.
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
A. Что посмотреть?
1.1
Резюме
Этот ноутбук читает данные из файлов в той же папке, решает 3 подзадачи и
сохраняет ответы в CSV:
• A1 favorite_genre → answer_a1.csv
• A2 actor_match → answer_a2.csv
• A4 next_in_session → answer_a3.csv
1.2
Файлы, которые должен видеть ноутбук
Вход (в одной папке с ноутбуком): - queries_A.csv - запросы (по 5 кандидатов c1..c5) -
items_A.csv - метаданные фильмов (genre, duration) - events_A.csv - события пользо-
вателей (open/finish/like) - item_meta_A.json - метаданные фильмов (списки актёров
actors) - sessions_A.json - сессии (внутри sessions[*].path)
Выход (создаётся рядом с ноутбуком):
Файл
Относительный путь Назначение
answer_a1.csv
./answer_a1.csv
ответы для favorite_genre
answer_a2.csv
./answer_a2.csv
ответы для actor_match
answer_a4.csv
./answer_a3.csv
ответы для next_in_session
1.3
Универсальный паттерн решения
Одинаковый каркас используется во всех трёх подзадачах:
1) melt переводит кандидатов c1..c5 из wide в long (по строке на кандидата).
2) merge(..., how='left') присоединяет признаки/скор, не теряя кандидатов.
3) Если есть списки (актёры, пары переходов), используем explode. Важно: пустые
списки дают NaN строку (это удобно, чтобы оставить кандидата и получить вклад
0).
4) Выбор
победителя
делается
сортировкой
sort_values
+
drop_duplicates('query_id') (первый в отсортированном порядке).
5) Сохранение - to_csv(index=False).
Ссылки на документацию pandas (официально):
• pandas.melt:
• DataFrame.explode:
• pandas.merge:
Страница 1 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
• DataFrame.sort_values:
• DataFrame.drop_duplicates:
• DataFrame.to_csv:
[1]:
import pandas as pd
import matplotlib.pyplot as plt
1.3.1
Быстрая проверка загрузки данных
Ниже читаем все файлы один раз, смотрим размеры таблиц и распределение query_type.
[2]:
Q = pd.read_csv('queries_A.csv')
I = pd.read_csv('items_A.csv')
E = pd.read_csv('events_A.csv')
M = pd.read_json('item_meta_A.json')[['item_id','actors']]
S = pd.read_json('sessions_A.json')
print('Q', Q.shape, 'I', I.shape, 'E', E.shape, 'M', M.shape, 'S', S.shape)
Q.query_type.value_counts()
Q (800, 8) I (260, 3) E (5334, 3) M (260, 2) S (180, 2)
[2]:
query_type
favorite_genre
400
actor_match
200
next_in_session
200
Name: count, dtype: int64
[3]:
vc = Q.query_type.value_counts().sort_index()
ax = vc.plot(kind='bar', title='Количество запросов по типам')
ax.set_xlabel('query_type')
ax.set_ylabel('count')
plt.tight_layout()
plt.show()
Страница 2 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
1.3.2
A1. favorite_genre
[4]: q = Q[Q.query_type ==
'favorite_genre'][['query_id','user_id','c1','c2','c3','c4','c5']]
e = E[['user_id','item_id','event_type']].copy()
e['w'] = e.event_type.map({'open': 1, 'finish': 2, 'like': 3})
e = e.merge(I[['item_id','genre']], on='item_id', how='left')
g = e.groupby(['user_id','genre'], as_index=False)['w'].sum()
c = q.melt(['query_id','user_id'], ['c1','c2','c3','c4','c5'],
value_name='item_id')[['query_id','user_id','item_id']]
c = c.merge(I[['item_id','genre','duration']], on='item_id', how='left')
c = c.merge(g, on=['user_id','genre'], how='left')
c['w'] = c.w.fillna(0)
c = c.sort_values(['query_id','w','duration','item_id'], ascending=[1,0,1,1])
a1 = c.drop_duplicates('query_id')[['query_id','item_id']]
a1.to_csv('answer_a1.csv', index=False)
a1.head()
[4]:
query_id item_id
400
1
1199
Страница 3 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
1201
2
1065
1602
3
1211
403
4
1086
4
5
1163
1.3.3
A2. actor_match
[5]:
q = Q[Q.query_type ==
'actor_match'][['query_id','user_id','c1','c2','c3','c4','c5']]
e = E[E.event_type.isin(['finish','like'])][['user_id','item_id']]
m = M[['item_id','actors']].copy()
m['actors'] = m.actors.apply(lambda x: x if isinstance(x, list) else [])
ua = e.merge(m, on='item_id', how='left')
ua = ua.explode('actors').dropna(subset=['actors'])
ua = ua.drop_duplicates(['user_id','actors'])
ua['hit'] = 1
ua = ua[['user_id','actors','hit']]
c = q.melt(['query_id','user_id'], ['c1','c2','c3','c4','c5'],
value_name='item_id')[['query_id','user_id','item_id']]
c = c.merge(m, on='item_id', how='left')
c = c.explode('actors')
c = c.drop_duplicates(['query_id','item_id','actors'])
c = c.merge(ua, on=['user_id','actors'], how='left')
c['hit'] = c.hit.fillna(0)
s = c.groupby(['query_id','item_id'], as_index=False)['hit'].sum()
s = s.sort_values(['query_id','hit','item_id'], ascending=[1,0,1])
a2 = s.drop_duplicates('query_id')[['query_id','item_id']]
a2.to_csv('answer_a2.csv', index=False)
a2.head()
[5]:
query_id item_id
4
401
1252
9
402
1255
14
403
1227
19
404
1216
24
405
1232
1.3.4
A3. next_in_session
[6]:
q = Q[Q.query_type ==
'next_in_session'][['query_id','user_id','c1','c2','c3','c4','c5']]
fin = E[E.event_type == 'finish'][['user_id','item_id']].drop_duplicates()
fin = fin.rename(columns={'item_id': 'from_item'})
Страница 4 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
t = S.explode('sessions').dropna(subset=['sessions'])
t['path'] = t.sessions.apply(lambda x: x.get('path', []))
t = t[['user_id','path']]
t['pair'] = t.path.apply(lambda p: list(zip(p[:-1], p[1:])))
t = t.explode('pair').dropna(subset=['pair'])
t['from_item'] = t.pair.str[0]
t['item_id'] = t.pair.str[1]
t['hit'] = 1
tr = t.groupby(['user_id','from_item','item_id'], as_index=False)['hit'].
↪→
sum()
c = q.melt(['query_id','user_id'], ['c1','c2','c3','c4','c5'],
value_name='item_id')[['query_id','user_id','item_id']]
c = c.merge(fin, on='user_id', how='left')
c = c.merge(tr, on=['user_id','from_item','item_id'], how='left')
c['hit'] = c.hit.fillna(0)
s = c.groupby(['query_id','item_id'], as_index=False)['hit'].sum()
s = s.sort_values(['query_id','hit','item_id'], ascending=[1,0,1])
a3 = s.drop_duplicates('query_id')[['query_id','item_id']]
a3.to_csv('answer_a3.csv', index=False)
a3.head()
[6]:
query_id item_id
2
601
1028
5
602
1071
11
603
1049
17
604
1101
20
605
1004
Страница 5 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
B. Сейсмоактивный остров
[ ]: import numpy as np
import pandas as pd
data = np.load('data_B.npy')
N = data.shape[0]
m = np.nanmean(data, axis=1)
# Попарная корреляция
sim = np.zeros((N, N))
for i in range(N):
for j in range(i, N):
mask = ~np.isnan(m[i]) & ~np.isnan(m[j])
a, b = m[i, mask] - m[i, mask].mean(), m[j, mask] - m[j, mask].mean()
d = np.sqrt((a**2).sum() * (b**2).sum())
if d > 0:
sim[i, j] = sim[j, i] = (a * b).sum() / d
# Каждому сэмплу - кластер с максимальной средней корреляцией
# Инициализация: жадно
labels = np.full(N, -1)
reps = []
for i in range(N):
best_sim, best_c = -1, -1
for c, r in enumerate(reps):
if sim[i, r] > best_sim:
best_sim = sim[i, r]
best_c = c
if best_sim > 0.35:
labels[i] = best_c
else:
labels[i] = len(reps)
reps.append(i)
# Уточнение: переназначаем по средней корреляции с кластером
for _ in range(10):
clusters = [np.where(labels == c)[0] for c in range(max(labels) + 1)]
changed = False
for i in range(N):
best_score, best_c = -2, labels[i]
for c, members in enumerate(clusters):
others = members[members != i]
score = sim[i, others].mean() if len(others) > 0 else sim[i,
members[0]]
if score > best_score:
best_score = score
best_c = c
if best_c != labels[i]:
labels[i] = best_c
Страница 6 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
changed = True
if not changed:
break
# Обновляем кластеры
clusters = [np.where(labels == c)[0] for c in range(max(labels) + 1)]
y = pd.read_csv('y.csv')['cluster'].values
from sklearn.metrics import adjusted_rand_score
print(f"Clusters: {len(set(labels))}, ARI = {adjusted_rand_score(y, labels):.
↪→
6f}")
pd.DataFrame({'ID': range(N), 'target': labels}).to_csv('submission_B.csv',
index=False)
Страница 7 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва,
25 марта 2026 г.
C. Ошибка новичка
[1]: import json
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from catboost import CatBoostClassifier
import warnings
from IPython.display import display
warnings.filterwarnings('ignore')
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
# Фиксируем сид для воспроизводимости всех шагов
SEED = 42
np.random.seed(SEED)
# Загрузка доступных файлов
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
solution = pd.read_csv('ytest.csv')
with open('map.json', 'r') as f:
map_data = json.load(f)
display(train.head())
display(test.head())
longitude
latitude target habitat_quality_score
biodiversity_index
\
0 -68.624132 -32.868160
0
38.746018
0.869221
1 -51.964445
1.978971
0
17.456343
3.414865
2 -50.585866
-5.300425
0
43.883046
1.930266
3 -64.666741 -32.100087
0
29.736058
1.724559
4 -72.512342
-5.718456
1
66.547123
9.257867
canopy_density soil_moisture_level forest_age_years
\
0
0.584679
6.031727
97.911830
1
0.327984
4.694215
169.739698
2
0.750885
7.864357
214.602112
3
0.322355
4.448480
275.153342
4
0.428812
3.822563
179.540444
tree_density_per_hectare annual_rainfall_mm habitat_fragmentation_index
\
0
396.493164
1293.066100
0.507221
1
350.579890
2353.814818
0.247614
2
237.872776
1147.090078
0.574802
3
458.725205
1252.207036
0.648601
4
346.391230
604.782917
0.732562
predator_pressure_score vegetation_complexity
forest_type
\
0
1.040142
3.0
tropical_rainforest
Страница 8 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
1
7.522185
9.0
dry_forest
2
3.255146
4.0
dry_forest
3
6.404152
9.0
tropical_rainforest
4
4.880406
2.0
dry_forest
climate_zone soil_type disturbance_level conservation_status
\
0
montane
peat
low
unprotected
1
temperate
loam
minimal
unprotected
2
subtropical
loam
moderate
unprotected
3
temperate
loam
low
buffer_zone
4
tropical
clay
low
unprotected
dominant_tree_species water_source_type temperature_variability
\
0
palm
stream
4.037666
1
pine
none
5.882945
2
palm
seasonal
3.366330
3
mahogany
stream
8.984897
4
bamboo
seasonal
3.966467
elevation_range human_activity_index canopy_height_avg
\
0
730.654928
0.075256
6.710190
1
316.780429
3.614124
30.770752
2
474.802210
2.251489
5.000000
3
187.292371
3.063365
16.284970
4
191.639490
3.888626
36.796504
seasonal_variation_score
0
6.874974
1
6.384739
2
5.690438
3
6.328448
4
5.780874
longitude
latitude habitat_quality_score biodiversity_index
\
0
-72.102869
19.626859
45.511580
3.743753
1
-50.441167
-8.396785
34.751298
4.712511
2
-58.372430
0.312577
73.162533
2.579567
3
-76.362920 -14.279388
86.832262
4.097908
4
-67.162159
6.450007
63.631579
4.895632
canopy_density soil_moisture_level forest_age_years
\
0
0.648016
3.260149
172.921350
1
0.226638
5.191137
357.568190
2
0.747227
7.533181
300.220431
3
0.602197
6.429846
325.612904
4
1.000000
4.502920
308.714905
tree_density_per_hectare annual_rainfall_mm habitat_fragmentation_index
\
0
226.157888
1177.791799
0.558007
1
429.731562
1922.536202
0.332591
Страница 9 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
2
144.840477
1844.822680
0.654825
3
323.532762
1571.667498
0.383862
4
693.209698
1688.758358
0.344897
predator_pressure_score vegetation_complexity
forest_type
\
0
7.795448
3.0
secondary_forest
1
5.712367
7.0
dry_forest
2
2.073700
1.0
cloud_forest
3
0.798348
5.0
dry_forest
4
1.378641
1.0
tropical_rainforest
climate_zone soil_type disturbance_level conservation_status
\
0
montane laterite
high
unprotected
1
temperate
sandy
moderate
protected
2
subtropical
loam
high
protected
3
tropical alluvial
minimal
buffer_zone
4
subtropical
sandy
minimal
protected
dominant_tree_species water_source_type temperature_variability
\
0
kapok
stream
5.205539
1
palm
seasonal
8.634001
2
pine
none
5.382953
3
pine
stream
8.282785
4
pine
stream
10.690082
elevation_range human_activity_index canopy_height_avg
\
0
318.967595
3.626041
23.686628
1
184.900374
1.830241
28.697284
2
536.571256
0.074063
26.366329
3
649.036995
0.000000
34.803212
4
388.522453
4.500977
45.697464
seasonal_variation_score ID
0
6.492307
0
1
4.313709
1
2
3.480140
2
3
3.417066
3
4
6.823274
4
1.4
Функция для визуализации
Создадим вспомогательную функцию для визуализации точек на карте с контуром бере-
говой линии (map.json).
[2]: def plot_map(points, coastline, color_by=None, figsize=(5, 5)):
"""
Визуализирует точки на карте с контуром береговой линии.
Параметры:
----------
points : pd.DataFrame
Страница 10 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
DataFrame с точками, должен содержать колонки 'longitude' и
'latitude'
coastline : list
Список координат береговой линии [[lon1, lat1], [lon2, lat2], ...]
color_by : str, optional
Название колонки для раскраски точек. Если None, все точки одного
цвета
figsize : tuple, optional
Размер фигуры (ширина, высота)
Пример использования:
--------------------
# Простая визуализация без раскраски
plot_map(train, coastline)
# С раскраской по таргету
plot_map(train, coastline, color_by='target')
# Изменение размера
plot_map(train, coastline, color_by='target', figsize=(16, 10))
"""
fig, ax = plt.subplots(figsize=figsize)
# Рисуем береговую линию
if coastline:
coastline_array = np.array(coastline)
# Рисуем все точки побережья как плотный scatter с квадратными
маркерами
ax.scatter(coastline_array[:, 0], coastline_array[:, 1],
s=1.5, marker='s', color='#CCCCCC', alpha=0.7,
label='Coastline')
# Рисуем точки
if color_by is None:
# Без раскраски - все точки одного цвета
ax.scatter(points['longitude'], points['latitude'],
s=50, alpha=0.6, edgecolors='black', linewidths=0.5,
label='Points')
else:
# С раскраской по указанной колонке
if points[color_by].dtype in ['object', 'category']:
# Категориальная переменная
unique_vals = points[color_by].unique()
colors = plt.cm.tab10(np.linspace(0, 1, len(unique_vals)))
for i, val in enumerate(unique_vals):
mask = points[color_by] == val
ax.scatter(points[mask]['longitude'],
points[mask]['latitude'],
Страница 11 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
s=50, alpha=0.6, edgecolors='black', linewidths=0.
↪→
5,
label=f'{color_by}={val}', color=colors[i])
else:
# Числовая переменная
scatter = ax.scatter(points['longitude'], points['latitude'],
c=points[color_by], cmap='RdYlGn',
s=50, alpha=0.6, edgecolors='black',
linewidths=0.5)
plt.colorbar(scatter, ax=ax, label=color_by)
ax.set_xlabel('Longitude')
ax.set_ylabel('Latitude')
ax.set_title(f'Карта точек (n={len(points)})')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print("Функция plot_map() готова к использованию")
Функция plot_map() готова к использованию
1.5
Демонстрация функции визуализации
Попробуем функцию на загруженных данных.
[3]: # Получаем coastline из map.json
coastline = map_data.get('coastline', [])
# Пример 1: Train без раскраски
plot_map(train, coastline)
# Пример 2: Train с раскраской по таргету
plot_map(train, coastline, color_by='target')
Страница 12 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
Страница 13 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
1.6
Шаг 1: Первый базовый подход
По условию задачи у нас есть train.csv (прямые наблюдения) и test.csv, где часть точек
добавлена по спутниковым данным.
Сначала строим честный baseline на всех признаках и смотрим F1 score на разметке из
ytest.csv. Это нужно как контрольная точка, чтобы понять, насколько модель склонна
переобучаться в исходной постановке.
[4]: # Функция для оценки качества
def evaluate(y_pred, solution):
"""Оценка F1-score: overall, public, private"""
from sklearn.metrics import f1_score
merged = solution.copy()
merged['pred'] = np.asarray(y_pred).reshape(-1)
# Считаем метрику на уже округленных предсказаниях (0/1)
merged['pred_label'] = np.clip(np.rint(merged['pred']), 0, 1).astype(int)
public_mask = merged['Usage'] == 'public'
private_mask = merged['Usage'] == 'private'
Страница 14 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
overall_f1 = f1_score(merged['target'], merged['pred_label'])
public_f1 = f1_score(merged.loc[public_mask, 'target'], merged.
↪→
loc[public_mask, 'pred_label'])
private_f1 = f1_score(merged.loc[private_mask, 'target'], merged.
↪→
loc[private_mask, 'pred_label'])
print(f"F1 overall: {overall_f1:.6f}")
print(f"F1 public: {public_f1:.6f}")
print(f"F1 private: {private_f1:.6f}")
return public_f1, private_f1, overall_f1
# Выделяем признаки (все кроме служебных)
feature_cols_numeric = [col for col in train.columns
if col not in ['ID', 'target', 'cluster', 'is_train']
and train[col].dtype in ['int64', 'float64']]
categorical_cols = train.select_dtypes(include=['object']).columns.tolist()
categorical_cols = [col for col in categorical_cols if col not in ['ID']]
all_features = feature_cols_numeric + categorical_cols
X_train = train[all_features]
y_train = train['target']
X_test = test[all_features]
from sklearn.model_selection import train_test_split
X_train_tr, X_train_val, y_train_tr, y_train_val = train_test_split(
X_train, y_train, test_size=0.2, random_state=SEED, stratify=y_train
)
model = CatBoostClassifier(
iterations=500,
learning_rate=0.1,
depth=12,
random_seed=SEED,
verbose=0,
)
model.fit(X_train_tr, y_train_tr, cat_features=categorical_cols)
y_pred_test_baseline = np.asarray(model.predict(X_test)).reshape(-1).
↪→
astype(int)
baseline_public, baseline_private, baseline_overall =
evaluate(y_pred_test_baseline, solution)
F1 overall: 0.646440
F1 public:
0.645707
F1 private: 0.647208
Страница 15 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
[5]:
from sklearn.metrics import f1_score
f1_score(solution.target.values, (solution.target.values * 0 + 0.5 >= 0.5).
↪→
astype(int))
[5]:
0.5270917803169922
1.7
Шаг 2: Анализ важности признаков
Связываем результат baseline с условием задачи. Если в топе важности оказываются коор-
динаты (longitude, latitude), модель может использовать географическую «подсказку»
вместо устойчивых экологических закономерностей. Это и есть первая типичная ошибка
новичка: неявная утечка/спуриация через признаки, которые слишком прямо кодируют
разделение.
[6]:
# Feature importance
importance_df = pd.DataFrame({
'feature': all_features,
'importance': model.get_feature_importance()
}).sort_values('importance', ascending=False)
print("\nTop 10 признаков:")
print(importance_df.head(10).to_string(index=False))
print("Это может означать, что модель переобучается на координаты.")
print("\nВНИМАНИЕ: longitude и latitude в топе важности!")
Top 10 признаков:
feature importance
latitude
25.842854
habitat_quality_score
18.788004
biodiversity_index
12.708726
longitude
9.644228
forest_type
6.298411
disturbance_level
5.888199
climate_zone
2.864203
conservation_status
1.980591
seasonal_variation_score
1.594778
human_activity_index
1.516413
Это может означать, что модель переобучается на координаты.
ВНИМАНИЕ: longitude и latitude в топе важности!
1.8
Шаг 3: Визуализация координат
Посмотрим, как распределены классы в пространстве координат.
[7]: # Используем нашу функцию для визуализации
print("Train данные с раскраской по таргету:")
plot_map(train, coastline, color_by='target', figsize=(10, 8))
Страница 16 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
# Анализ распределения классов
print("\n" + "="*70)
print("АНАЛИЗ КООРДИНАТ")
print("="*70)
# Статистика по долготе
print("\nРаспределение классов по долготе:")
for target_val in [0, 1]:
subset = train[train['target'] == target_val]
print(f"\nКласс {target_val}:")
print(f" Longitude: min={subset['longitude'].min():.2f},
max={subset['longitude'].max():.2f}, mean={subset['longitude'].mean():.2f}")
print(f" Latitude: min={subset['latitude'].min():.2f},
max={subset['latitude'].max():.2f}, mean={subset['latitude'].mean():.2f}")
print("\n" + "="*70)
print("ВЫВОД: Классы четко разделены по координатам!")
print("
Модель запомнила географическую границу между классами.")
print("
В реальном тесте эта закономерность не работает.")
print("
РЕШЕНИЕ: Нужно убрать longitude и latitude из признаков.")
print("="*70)
Train данные с раскраской по таргету:
Страница 17 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
======================================================================
АНАЛИЗ КООРДИНАТ
======================================================================
Распределение классов по долготе:
Класс 0:
Longitude: min=-91.65, max=-39.05, mean=-64.11
Latitude: min=-55.12, max=22.89, mean=-10.67
Класс 1:
Longitude: min=-90.20, max=-38.22, mean=-62.39
Latitude: min=-29.95, max=-0.10, mean=-15.78
======================================================================
ВЫВОД: Классы четко разделены по координатам!
Модель запомнила географическую границу между классами.
В реальном тесте эта закономерность не работает.
РЕШЕНИЕ: Нужно убрать longitude и latitude из признаков.
======================================================================
1.9
Шаг 4: Убираем координаты и переобучаем модель
Исправляем первую ошибку: убираем longitude и latitude, чтобы модель опиралась на
содержательные признаки среды. Снова считаем F1 score и сравниваем с baseline. Если
качество растет, значит координаты действительно мешали обобщению.
[8]: # Убираем координаты
features_no_coords = [f for f in all_features if f not in ['longitude',
'latitude']]
categorical_cols_filtered = [c for c in categorical_cols if c in
features_no_coords]
X_train_no_coords = train[features_no_coords]
X_test_no_coords = test[features_no_coords]
X_train_tr_no_coords, X_train_val_no_coords, y_train_tr_no_coords,
y_train_val_no_coords = train_test_split(
X_train_no_coords, y_train, test_size=0.2, random_state=SEED,
stratify=y_train
)
model.fit(X_train_tr_no_coords, y_train_tr_no_coords,
cat_features=categorical_cols_filtered)
y_pred_test_no_coords = np.asarray(model.predict(X_test_no_coords)).
↪→
reshape(-1).astype(int)
Страница 18 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
nocoords_public, nocoords_private, nocoords_overall =
evaluate(y_pred_test_no_coords, solution)
F1 overall: 0.783972
F1 public:
0.798155
F1 private: 0.769591
1.10
Шаг 5: Детектор train vs test (поиск сдвига распределения)
Проверяем вторую ошибку новичка из условия: игнорирование того, что train и часть
test собраны разными способами. Если модель легко отличает train от test, это признак
domain shift (распределения различаются). Тогда у части test-точек стандартная модель
может быть слишком уверенной и ошибаться системно.
[9]: from catboost import cv, Pool
# Создаем датасет для классификации train vs test
X_combined = pd.concat([X_train_no_coords, X_test_no_coords], axis=0)
y_combined = np.concatenate([np.ones(len(X_train_no_coords)), np.
↪→
zeros(len(X_test_no_coords))])
model_ood = CatBoostClassifier(
iterations=50,
learning_rate=0.1,
depth=6,
random_seed=SEED,
verbose=0
)
cv_data = Pool(data=X_combined, label=y_combined,
cat_features=categorical_cols_filtered)
_ = cv(
pool=cv_data,
params={
'iterations': 50,
'learning_rate': 0.1,
'depth': 6,
'loss_function': 'Logloss',
'eval_metric': 'AUC',
'random_seed': SEED,
'verbose': False
},
fold_count=5,
shuffle=True,
stratified=True,
partition_random_seed=SEED
)
model_ood.fit(X_combined, y_combined, cat_features=categorical_cols_filtered)
test_similarity_scores = model_ood.predict(X_test_no_coords,
prediction_type='Probability')[:, 1]
Страница 19 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
plt.figure(figsize=(10, 5))
plt.hist(test_similarity_scores, bins=100, alpha=0.7, edgecolor='black')
plt.xlabel('Вероятность принадлежности к train')
plt.ylabel('Количество объектов')
plt.title('Train/Test Classifier: Распределение вероятностей на test')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Training on fold [0/5]
bestTest = 0.6328465347
bestIteration = 9
Training on fold [1/5]
bestTest = 0.6250990099
bestIteration = 4
Training on fold [2/5]
bestTest = 0.6888
bestIteration = 6
Training on fold [3/5]
bestTest = 0.6909022556
bestIteration = 11
Training on fold [4/5]
bestTest = 0.6967669173
bestIteration = 16
Страница 20 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
1.11
Шаг 6: OOD-корректировка предсказаний
Используем train/test-детектор как индикатор OOD-точек (похожести тестовой точки на
train). Для точек с очень низкой похожестью понижаем вероятность класса 1 и снова
считаем только F1 score.
Так мы адресуем вторую ошибку: учитываем сдвиг распределения и делаем предсказания
устойчивее на «спутниковой» части теста.
[10]: print("\nКорректируем предсказания для OOD объектов...")
print("="*70)
# Порог OOD: точки с низкой похожестью на train считаем сдвинутыми по
распределению
ood_threshold = 0.07
y_pred_corrected = y_pred_test_no_coords.copy()
ood_mask = test_similarity_scores < ood_threshold
y_pred_corrected[ood_mask] = 0
# Выводим F1-score для каждого подхода: overall/public/private
_, _, _ = evaluate(y_pred_test_baseline, solution)
_, _, _ = evaluate(y_pred_test_no_coords, solution)
_, _, corrected_overall = evaluate(y_pred_corrected, solution)
Корректируем предсказания для OOD объектов...
======================================================================
F1 overall: 0.646440
F1 public:
0.645707
F1 private: 0.647208
F1 overall: 0.783972
F1 public:
0.798155
F1 private: 0.769591
F1 overall: 0.918549
F1 public:
0.918340
F1 private: 0.918768
[11]: # Сохраняем submission с лучшими доступными предсказаниями
final_pred = y_pred_corrected
submission = pd.DataFrame({
'ID': test['ID'].values,
'target': (final_pred > 0.5) * 1
})
submission.to_csv('author_submission.csv', index=False)
Страница 21 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
D. Наследие Человечества
[1]: # Шаг 0. Импорты
import csv
import json
from pathlib import Path
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.preprocessing import StandardScaler
# Фиксируем генератор случайных чисел для воспроизводимости
rng = np.random.default_rng(42)
1.12
Разбор решения по шагам
Решения идут по порядку и усложняются шаг за шагом: 1. Решение 0: IOU/Jaccard по
словам. 2. Решение 1: unigram TF-IDF. 3. Решение 2: word+char TF-IDF. 4. Решение 3:
решение 2 + greedy one-to-one. 5. Решение 4: решение 3 + logreg-reranker. 6. Решение 5:
решение 4 + greedy one-to-one.
1.12.1
Шаг 1. Загрузка train/test/ytest
Читаем train.json и test.json, восстанавливаем train-пары, а true_test и разбиение
Public/Private строим по ytest.csv через соответствия left_id ↔ right_id и колон-
ку Usage.
[2]: # Шаг 1. Загрузка train/test/ytest (ещё компактнее)
base_dir = Path("/Users/aguschin/Git/uni/vsosh/zakl/nlp")
train_json_path, test_json_path, ytest_csv_path = (
base_dir / "train.json",
base_dir / "test.json",
base_dir / "ytest.csv",
)
def split_poem(text):
lines = [line.strip() for line in text.split("\n") if line.strip()]
cut = min(8, len(lines) - 1)
if cut < 1:
return None, None
left, right = "\n".join(lines[:cut]).strip(), "\n".join(lines[cut:]).
↪→
strip()
return (left, right) if left and right else (None, None)
# train: из полных текстов -> пары половинок -> shuffle правых
train_pairs = [split_poem(text) for text in json.loads(train_json_path.
↪→
read_text(encoding="utf-8"))]
train_pairs = [(left, right) for left, right in train_pairs if left and
right]
Страница 22 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
left_parts = [left for left, _ in train_pairs]
right_parts = [right for _, right in train_pairs]
train_idx = np.arange(len(left_parts), dtype=int)
perm_train = np.random.default_rng(123).permutation(len(left_parts))
left_train = left_parts
right_train_shuffled = [right_parts[i] for i in perm_train]
true_train = np.empty(len(left_parts), dtype=int)
true_train[perm_train] = np.arange(len(left_parts))
n_train = len(left_train)
# test: готовые left/right
test_data = json.loads(test_json_path.read_text(encoding="utf-8"))
left_items, right_items = test_data["left"], test_data["right"]
left_test = [item["left_text"] for item in left_items]
left_ids = [item["left_id"] for item in left_items]
right_test_shuffled = [item["right_text"] for item in right_items]
right_ids = [item["right_id"] for item in right_items]
n_test = len(left_test)
# ytest: left_id -> right_id и Usage
with open(ytest_csv_path, "r", encoding="utf-8") as f:
ytest_rows = [row for row in csv.DictReader(f)]
left_to_right = {}
left_to_usage = {}
for row in ytest_rows:
left_id = (row.get("left_id") or "").strip()
right_id = (row.get("right_id") or "").strip()
usage = (row.get("Usage") or "Private").strip().title()
if not left_id or not right_id:
continue
if usage not in {"Public", "Private"}:
usage = "Private"
left_to_right[left_id] = right_id
left_to_usage[left_id] = usage
right_id_to_pos = {rid: j for j, rid in enumerate(right_ids)}
true_test = np.array([right_id_to_pos[left_to_right[lid]] for lid in
left_ids], dtype=int)
usage_test = np.array([left_to_usage.get(lid, "Private") for lid in
left_ids], dtype=object)
public_mask = usage_test == "Public"
private_mask = usage_test == "Private"
# проверки и служебные переменные
assert all(lid in left_to_right for lid in left_ids), "Не все left_id из test
есть в ytest.csv"
assert all(left_to_right[lid] in right_id_to_pos for lid in left_ids),
"Некоторые right_id из ytest.csv отсутствуют в test.json"
vowels = set("аеёиоуыэюя")
Страница 23 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
test_idx = np.arange(n_test, dtype=int) + 10_000_000
overlap = len(set(train_idx.tolist()) & set(test_idx.tolist()))
assert overlap == 0, "train/test overlap detected"
print(f"Источник train: {train_json_path}")
print(f"Источник test : {test_json_path}")
print(f"Источник gt
: {ytest_csv_path}")
print(f"Всего пар: Train: {n_train} | Test: {n_test}")
print(f"Usage split: Public={int(public_mask.sum())} |
Private={int(private_mask.sum())}")
print(f"Честность split: overlap(train,test)={overlap}\n")
Источник train: /Users/aguschin/Git/uni/vsosh/zakl/nlp/train.json
Источник test : /Users/aguschin/Git/uni/vsosh/zakl/nlp/test.json
Источник gt
: /Users/aguschin/Git/uni/vsosh/zakl/nlp/ytest.csv
Всего пар: Train: 2000 | Test: 640
Usage split: Public=320 | Private=320
Честность split: overlap(train,test)=0
1.12.2
Решение 0 - IOU/Jaccard по словам
Базовый скор: для каждой пары половинок считаем пересечение слов и делим на объеди-
нение. Оценка/выбор пары: top1 через argmax (greedy matching не используется).
[3]: # Шаг 2. Решение 0: IOU/Jaccard через split()
# Решение 0: базовый скор по пересечению слов
def _masked_acc(pred_idx: np.ndarray, true_idx: np.ndarray, mask: np.
↪→
ndarray):
return float((pred_idx[mask] == true_idx[mask]).mean()) if mask.any()
else float("nan")
# Функция: считает единый score через argmax (Overall/Public/Private)
def evaluate_similarity_argmax(sim: np.ndarray, true_idx: np.ndarray):
pred = sim.argmax(axis=1)
score = float((pred == true_idx).mean())
score_public = _masked_acc(pred, true_idx, public_mask)
score_private = _masked_acc(pred, true_idx, private_mask)
return {
"score": score,
"score_public": score_public,
"score_private": score_private,
}
# Функция: превращает каждый текст в множество токенов
def _token_sets(texts):
return [set(text.split()) for text in texts]
# Функция: строит матрицу Jaccard/IOU для всех пар left-right
Страница 24 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
def _jaccard_matrix(left_sets, right_sets):
n_left = len(left_sets)
n_right = len(right_sets)
sim = np.zeros((n_left, n_right), dtype=np.float32)
for i, lset in enumerate(left_sets):
for j, rset in enumerate(right_sets):
union = lset | rset
if union:
sim[i, j] = len(lset & rset) / len(union)
return sim
word_train_l = _token_sets(left_train)
word_train_r = _token_sets(right_train_shuffled)
word_test_l = _token_sets(left_test)
word_test_r = _token_sets(right_test_shuffled)
sim0_test = _jaccard_matrix(word_test_l, word_test_r)
metrics0 = evaluate_similarity_argmax(sim0_test, true_test)
print(
f"0_iou_argmax: "
f"score={metrics0['score']:.4f} (pub={metrics0['score_public']:.4f},
priv={metrics0['score_private']:.4f})"
)
0_iou_argmax: score=0.1734 (pub=0.1594, priv=0.1875)
1.12.3
Решение 1 - unigram TF-IDF
Улучшаем решение 0: заменяем простое пересечение слов на TF-IDF-представление слов
и косинусную близость.
[4]: # Шаг 2. Решение 1: unigram TF-IDF
# Решение 1: улучшаем решение 0 с помощью TF-IDF признаков
vec1 = TfidfVectorizer(ngram_range=(1, 1), min_df=5, max_df=0.95,
sublinear_tf=False)
vec1.fit(left_train + right_train_shuffled)
sim1_test = cosine_similarity(vec1.transform(left_test), vec1.
↪→
transform(right_test_shuffled))
metrics1 = evaluate_similarity_argmax(sim1_test, true_test)
print(
f"1_unigram_tfidf: "
f"score={metrics1['score']:.4f} (pub={metrics1['score_public']:.4f},
priv={metrics1['score_private']:.4f})"
)
1_unigram_tfidf: score=0.2734 (pub=0.2562, priv=0.2906)
1.12.4
Решение 2 - word+char TF-IDF
Улучшаем решение 1: смешиваем similarity из word TF-IDF и char TF-IDF, затем считаем
argmax-метрики.
Страница 25 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
[5]:
# Шаг 3. Решение 2: word+char TF-IDF
# Функция: строит blended similarity из word и char TF-IDF
def _build_tfidf_blend(left_train, right_train, left_eval, right_eval, cfg,
alpha):
vec_word = TfidfVectorizer(analyzer="word", **cfg["word"] )
vec_char = TfidfVectorizer(analyzer="char_wb", **cfg["char"] )
vec_word.fit(left_train + right_train)
vec_char.fit(left_train + right_train)
sim_word = cosine_similarity(vec_word.transform(left_eval), vec_word.
↪→
transform(right_eval))
sim_char = cosine_similarity(vec_char.transform(left_eval), vec_char.
↪→
transform(right_eval))
return alpha * sim_word + (1.0 - alpha) * sim_char
cfg2b = {
"word": dict(ngram_range=(1, 2), min_df=2, max_df=0.95,
sublinear_tf=True),
"char": dict(ngram_range=(2, 5), min_df=1, max_df=0.99,
sublinear_tf=True),
}
alpha2b = 0.25
# Решение 2: улучшаем решение 1 смешением word/char признаков
sim2b_train = _build_tfidf_blend(left_train, right_train_shuffled,
left_train, right_train_shuffled, cfg2b, alpha2b)
sim2b_test = _build_tfidf_blend(left_train, right_train_shuffled, left_test,
right_test_shuffled, cfg2b, alpha2b)
metrics2 = evaluate_similarity_argmax(sim2b_test, true_test)
print(
f"2_word_char_tfidf: "
f"score={metrics2['score']:.4f} (pub={metrics2['score_public']:.4f},
priv={metrics2['score_private']:.4f})"
)
2_word_char_tfidf: score=0.4938 (pub=0.4969, priv=0.4906)
1.12.5
Решение 3 - TF-IDF + greedy one-to-one
Берём матрицу скоров решения 2 и добавляем жадное взаимно-однозначное сопоставление
для роста one2one.
[6]:
# Функция: строит жадное взаимно-однозначное соответствие
def greedy_one2one_matching(sim: np.ndarray):
n = sim.shape[0]
flat_order = np.argsort(sim, axis=None)[::-1]
pred = np.full(n, -1, dtype=int)
used_left = np.zeros(n, dtype=bool)
used_right = np.zeros(n, dtype=bool)
matched = 0
for idx in flat_order:
Страница 26 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
i, j = divmod(idx, n)
if (not used_left[i]) and (not used_right[j]):
pred[i] = j
used_left[i] = True
used_right[j] = True
matched += 1
if matched == n:
break
return pred
# Функция: считает единый score через greedy
def evaluate_similarity_greedy(sim: np.ndarray, true_idx: np.ndarray):
pred = greedy_one2one_matching(sim)
score = float((pred == true_idx).mean())
score_public = _masked_acc(pred, true_idx, public_mask)
score_private = _masked_acc(pred, true_idx, private_mask)
return {
"score": score,
"score_public": score_public,
"score_private": score_private,
}
# Решение 3: добавляем greedy к решению 2
metrics3 = evaluate_similarity_greedy(sim2b_test, true_test)
print(
f"3_tfidf_greedy: "
f"score={metrics3['score']:.4f} (pub={metrics3['score_public']:.4f},
priv={metrics3['score_private']:.4f})"
)
3_tfidf_greedy: score=0.5062 (pub=0.5094, priv=0.5031)
1.12.6
Решение 4 - logreg reranker
Улучшаем решение 3: добавляем pairwise-фичи и логистическую регрессию, которая пе-
реоценивает кандидатов перед argmax.
[7]: # Коротко: считаем статистики половинок и собираем pairwise-feature cube
def _half_stats(texts):
stats = []
for text in texts:
lines = [line.strip() for line in text.split("\n") if line.strip()]
or [text.strip()]
words = ["".join(ch for ch in w if ch.isalpha()) for w in text.
↪→
replace("\n", " ").split()]
words = [w for w in words if w]
letters = [ch for ch in text if ch.isalpha()]
v_all = [sum(1 for ch in line.lower() if ch in vowels) for line in
lines]
v_edge = [sum(1 for ch in line.lower() if ch in vowels) for line in
lines[-4:]]
Страница 27 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
cap_ratio = (sum(1 for w in words if w[0].isupper()) / len(words)) if
words else 0.0
latin_ratio = (sum(1 for ch in letters if 'a' <= ch.lower() <= 'z') /
len(letters)) if letters else 0.0
stats.append([len(lines), np.mean([len(line) for line in lines]), np.
↪→
mean(v_all), np.mean(v_edge), cap_ratio, latin_ratio])
return np.asarray(stats, dtype=np.float32)
# Коротко: переводим разницу скалярного признака в similarity
def _pair_sim(left_vals, right_vals):
diff = np.abs(left_vals[:, None] - right_vals[None, :])
norm = np.maximum(left_vals[:, None], right_vals[None, :])
return 1.0 - diff / (norm + 1e-12)
# Коротко: формируем обучающие пары (true + hard negatives + random
negatives)
def _sample_pairs(cube, true_idx, hard_source_sim, hard_k=8, rand_k=8,
random_state=42):
rng_local = np.random.default_rng(random_state)
n = cube.shape[0]
rows, labels = [], []
for i in range(n):
j_true = true_idx[i]
rows.append(cube[i, j_true]); labels.append(1)
hard_neg = [j for j in np.argsort(-hard_source_sim[i]) if j !=
j_true][:hard_k]
forbidden = set(hard_neg + [j_true])
pool = np.array([j for j in range(n) if j not in forbidden],
dtype=int)
rand_neg = rng_local.choice(pool, size=min(rand_k, len(pool)),
replace=False).tolist() if len(pool) else []
for j in hard_neg + rand_neg:
rows.append(cube[i, j]); labels.append(0)
return np.asarray(rows, dtype=np.float32), np.asarray(labels, dtype=np.
↪→
int32)
left_train_feat = _half_stats(left_train)
right_train_feat = _half_stats(right_train_shuffled)
left_test_feat = _half_stats(left_test)
right_test_feat = _half_stats(right_test_shuffled)
train_maps = [sim2b_train] + [_pair_sim(left_train_feat[:, k],
right_train_feat[:, k]) for k in range(left_train_feat.shape[1])] +
[_jaccard_matrix(word_train_l, word_train_r).astype(np.float32)]
test_maps = [sim2b_test] + [_pair_sim(left_test_feat[:, k], right_test_feat[:
↪→
, k]) for k in range(left_test_feat.shape[1])] +
[_jaccard_matrix(word_test_l, word_test_r).astype(np.float32)]
cube_train = np.stack(train_maps, axis=2).astype(np.float32)
cube_test = np.stack(test_maps, axis=2).astype(np.float32)
Страница 28 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
x_train, y_train = _sample_pairs(cube_train, true_train, sim2b_train,
hard_k=8, rand_k=8, random_state=42)
scaler = StandardScaler()
model = LogisticRegression(C=0.03, max_iter=2000, class_weight="balanced",
solver="lbfgs", random_state=42)
model.fit(scaler.fit_transform(x_train), y_train)
flat_scores = model.predict_proba(scaler.transform(cube_test.reshape(-1,
cube_test.shape[-1])))[:, 1]
test_scores_4 = flat_scores.reshape(cube_test.shape[0], cube_test.shape[1])
alpha4 = 0.82
sim4_test = alpha4 * test_scores_4 + (1.0 - alpha4) * sim2b_test
metrics4 = evaluate_similarity_argmax(sim4_test, true_test)
print(
f"4_logreg_rerank: "
f"score={metrics4['score']:.4f} (pub={metrics4['score_public']:.4f},
priv={metrics4['score_private']:.4f})"
)
4_logreg_rerank: score=0.5203 (pub=0.5031, priv=0.5375)
1.12.7
Решение 5 - logreg reranker + greedy
Финальное улучшение: к score-матрице решения 4 снова применяем greedy one-to-one для
максимального one2one.
[8]:
# Решение 5: добавляем greedy к скалам решения 4
metrics5 = evaluate_similarity_greedy(sim4_test, true_test)
print(
f"5_logreg_rerank_greedy: "
f"score={metrics5['score']:.4f} (pub={metrics5['score_public']:.4f},
priv={metrics5['score_private']:.4f})"
)
5_logreg_rerank_greedy: score=0.5656 (pub=0.5687, priv=0.5625)
[10]:
# Сохраняем сабмит для автора из лучшего решения
sim_test = sim4_test
pred_idx = sim_test.argmax(axis=1)
author_submission_path = base_dir / "author_submission.csv"
with open(author_submission_path, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["left_id", "right_id"])
for i, lid in enumerate(left_ids):
writer.writerow([lid, right_ids[int(pred_idx[i])]])
print(f"Saved {author_submission_path}")
Saved /Users/aguschin/Git/uni/vsosh/zakl/nlp/author_submission.csv
Страница 29 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
1.12.8
Шаг 5. Результаты матчинга
Показываем только примеры матчинга для лучшего текущего решения: лучшие и худшие
пары.
[12]: # Шаг 5. Результаты матчинга по квантилям для лучшего решения
def _preview_text(text, max_chars=220, max_lines=8):
lines = [line.strip() for line in text.split("\n") if line.strip()][:
↪→
max_lines]
preview = "\n".join(lines)
return preview[:max_chars] + ("..." if len(preview) > max_chars else "")
sim_test = sim4_test
pred_idx = sim_test.argmax(axis=1)
quantiles_to_show = [0.99, 0.75, 0.5, 0.25, 0.0] # можно менять вручную
if not quantiles_to_show or any(q < 0 or q > 1 for q in quantiles_to_show):
raise ValueError("Квантили должны быть в [0, 1], список не должен быть
пустым")
match_scores = sim_test[np.arange(len(left_test)), pred_idx]
used = set()
def pick_idx_for_quantile(q):
target = float(np.quantile(match_scores, q))
order = np.argsort(np.abs(match_scores - target))
idx = next((int(i) for i in order if int(i) not in used), int(order[0]))
used.add(idx)
return idx, target
print(f"Матчинг для лучшего решения")
print(f"Квантили: {quantiles_to_show}")
print()
for q in quantiles_to_show:
i, target = pick_idx_for_quantile(q)
j = int(pred_idx[i])
print(f"=== q={q:.2f} | target={target:.4f} | sim={match_scores[i]:.4f}
===")
print(f"left={i} -> pred_right={j} | true={true_test[i]} | correct={j ==
true_test[i]}")
print("LEFT :", _preview_text(left_test[i]))
print("RIGHT:", _preview_text(right_test_shuffled[j]))
print()
Матчинг для лучшего решения
Квантили: [0.99, 0.75, 0.5, 0.25, 0.0]
=== q=0.99 | target=0.9055 | sim=0.9076 ===
left=383 -> pred_right=628 | true=628 | correct=True
LEFT : Человек живет на белом свете.
Страница 30 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
Где - не знаю. Суть совсем не в том.
Я - лежу в пристрелянном кювете,
Он - с мороза входит в теплый дом.
Человек живет на белом свете,
Он - в квартиру поднялся уже.
Я - лежу в пристрелянном ...
RIGHT: Человек живет на белом свете
Он - в квартире зажигает свет
Я - лежу в пристрелянном кювете,
Я - вмерзаю в ледяной кювет.
Снег не тает. Губы, щеки, веки
Он засыпал. И велит дрожать...
С думой о далеком человеке
Легче до а...
=== q=0.75 | target=0.7871 | sim=0.7870 ===
left=208 -> pred_right=508 | true=508 | correct=True
LEFT : Бронепоезда взвывают вдруг,
Стылый ветер грудью разрывая.
Бронепоезда идут на юг
Вдоль твоих перронов,
Лозовая!
Звезды первую звезду зовут.
Дым заката холоден и розов.
Над бронеплощадками плывут
RIGHT: Бескозырки черные матросов.
Говорит, гремит, вздыхает бронь
Отдаленно
и громоподобно.
И горит на станции огонь,
Керосиновый огонь бездомный.
Лист осенний, запоздавший лист,
Братьев в путь-дорогу созывает.
=== q=0.50 | target=0.7115 | sim=0.7113 ===
left=342 -> pred_right=350 | true=350 | correct=True
LEFT : Славянка тихая, сколь ток приятен твой,
Когда, в осенний день, в твои глядятся воды
Холмы, одетые последнею красой
Полуотцветшия природы.
Спешу к твоим брегам... свод неба тих и чист;
При свете солнечном прохлада повевае...
RIGHT: Иду под рощею излучистой тропой;
Что шаг, то новая в глазах моих картина;
То вдруг сквозь чащу древ мелькает предо мной,
Как в дыме, светлая долина;
То вдруг исчезло все... окрест сгустился лес;
Все дико вкруг меня, и су...
=== q=0.25 | target=0.6497 | sim=0.6498 ===
left=532 -> pred_right=415 | true=415 | correct=True
Страница 31 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
LEFT : Посвящается Феллини
Мертвец играл на дудочке,
По городу гулял,
И незнакомой дурочке
Он руку предлагал.
А дурочка, как Золушка,
Ему в глаза глядит,-
Он говорит о золоте,
RIGHT: О славе говорит.
Мертвец, певец и умница,
Его слова просты -
Пусты ночные улицы,
И площади пусты.
«Мне больно, мне невесело,
Мне холодно зимой,
Возьми меня невестою,
=== q=0.00 | target=0.1137 | sim=0.1137 ===
left=533 -> pred_right=373 | true=329 | correct=False
LEFT : Нет.
Это неправда.
Нет!
И ты?
Любимая,
за что,
за что же?!
Хорошо -
RIGHT: Что нашу грусть -
В листы,
И груз - в цветы
Всего за только всхруст
Руки
В руке:
Игру.
Индус, а может Златоуст
Страница 32 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
E. Подозрительные пирожные
Решение состояит из следующих этапов: 1. Загружаем предобученную CNN и полу-
чаем эмбеддинги для картинок. 2. Вычисляем для каждой картинки outlier-метрики
robust_mahalanobis, global_knn, class_knn. 3. Аггрегируем метрики с весами (2.0,
1.5, 0.5). 4. Сортируем картинки по значению матрики, выбираем top-K и сохраняем
в submission_author.csv.
[1]:
import json
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
ARTIFACTS_DIR = "."
TEST_PACKAGE = f"{ARTIFACTS_DIR}/public_test_package.npz"
TEST_META = f"{ARTIFACTS_DIR}/public_test_meta.json"
WEIGHTS_PATH = f"{ARTIFACTS_DIR}/model_weights.pt"
SUBMISSION_PATH = "submission.csv"
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("Device:", DEVICE)
Device: cpu
Код сети и получения эмбеддингов картинок:
[2]:
class SmallCNN(nn.Module):
def __init__(self, n_classes=10):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(1, 16, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(2),
nn.Conv2d(16, 32, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
)
self.fc1 = nn.Linear(64 * 8 * 8, 64)
self.act = nn.ReLU(inplace=True)
self.fc2 = nn.Linear(64, n_classes)
def forward(self, x):
z = self.features(x)
z = z.flatten(1)
h = self.act(self.fc1(z))
logits = self.fc2(h)
return logits, h
Страница 33 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
def infer_embeddings_and_preds(model, x_np, batch_size=256):
ds = TensorDataset(torch.from_numpy(x_np).float())
loader = DataLoader(ds, batch_size=batch_size, shuffle=False)
all_emb, all_pred = [], []
model.eval()
with torch.no_grad():
for (xb,) in loader:
xb = xb.to(DEVICE)
logits, emb = model(xb)
all_emb.append(emb.cpu().numpy())
all_pred.append(logits.argmax(dim=1).cpu().numpy())
emb = np.concatenate(all_emb, axis=0).astype(np.float32)
pred = np.concatenate(all_pred, axis=0).astype(np.int64)
return emb, pred
state = torch.load(WEIGHTS_PATH, map_location=DEVICE)
n_classes = int(state["fc2.weight"].shape[0]) if "fc2.weight" in state else
10
model = SmallCNN(n_classes=n_classes)
model.load_state_dict(state)
model = model.to(DEVICE).eval()
print("Model loaded")
Model loaded
[3]: test = np.load(TEST_PACKAGE)
x_test = test["images"].astype(np.float32)
K = 1000
emb_test, pred_test = infer_embeddings_and_preds(model, x_test)
n_test = len(x_test)
print(f"n_test={n_test}, K={K}, emb_dim={emb_test.shape[1]}")
n_test=10000, K=1000, emb_dim=64
1.12.9
Идея решения
Будем искать выбросы в пространстве эмбеддингов нейросети.
Предполагаем, что после прохождения через обученную модель объекты одного и того же
класса образуют в пространстве признаков компактные кластеры. Тогда выбросы можно
искать как точки, которые расположены “нетипично” относительно остальных точек.
Для этого используются несколько типов метрик:
1. Global kNN score.
Для каждого объекта считается среднее расстояние до его ближайших соседей среди
всех объектов датасета.
Если объект находится изолированно, его score будет большим.
2. Class kNN score.
Аналогично предыдущему пункту, но соседи ищутся только среди объектов того же
Страница 34 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
предсказанного класса.
Это позволяет находить точки, которые выглядят необычно именно внутри своего
класса, даже если глобально они не слишком далеки от других.
3. Robust Mahalanobis score внутри класса.
Для каждого предсказанного класса оценивается среднее µ и матрица ковариаций Σ
эмбеддингов, после чего для каждого объекта считается расстояние Махаланобиса
до центра своего класса. Расстояние Махаланобиса считается так:
(xi - µ)T Σ-1(xi - µ)
Идея использования ковариации в том, что Махаланобис смотрит на точку относи-
тельно формы распределения класса. Иногда точку-выброс можно поймать тем, что
она отклоняется не по длине, а по “неправильному направлению” в пространстве.
Чтобы выбросы не искажали изначальные оценки µ и Σ по выборке, используем ро-
бастную схему: на каждой итерации временно отбрасываем самые далёкие точки, и
оцениваем параметры только по оставшемуся “ядру” класса.
Таким образом, решение опирается на следующую гипотезу:
выбросы - это объекты, которые в пространстве признаков либо далеки от
типичного распределения своего класса, либо плохо вписываются в локальную
структуру соседей.
Примечание: без использования расстояния Махаланобиса можно получить до 80% бал-
лов за задачу.
Ниже реализованы функции метрик:
[4]: import numpy as np
def score_robust_mahalanobis(emb, pred, lam=0.15, trim_frac=0.10, iters=2):
"""
Считает робастный Mahalanobis score внутри каждого предсказанного класса.
Идея:
- для каждого класса оцениваем "нормальное" распределение эмбеддингов;
- затем для каждого объекта считаем расстояние Махаланобиса до центра
класса;
- чтобы выбросы не портили оценку среднего и ковариации,
несколько раз отбрасываем самые далёкие точки и пересчитываем
статистики.
Параметры:
- emb: матрица эмбеддингов shape [N, d]
- pred: предсказанные классы shape [N]
- lam: коэффициент регуляризации ковариации
- trim_frac: доля самых далёких точек, временно исключаемых при trimming
- iters: число итераций робастного пересчёта
"""
d = emb.shape[1] # размерность пространства признаков
out = np.zeros(len(emb), dtype=np.float64) # итоговый score для всех
объектов
eye = np.eye(d, dtype=np.float64) # единичная матрица для регуляризации
Страница 35 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
# Обрабатываем каждый класс отдельно
for c in np.unique(pred):
m = pred == c
# маска объектов класса c
h = emb[m].astype(np.float64) # эмбеддинги только этого класса
n = len(h)
# Если объектов слишком мало, робастную оценку делать ненадёжно.
# Тогда просто считаем обычное расстояние Махаланобиса.
if n < 8:
mu = h.mean(axis=0, keepdims=True)
# центр класса
cen = h - mu
# центрированные эмбеддинги
# Ковариация + диагональная регуляризация для устойчивости
cov = (cen.T @ cen) / max(1, n - 1) + lam * eye
# Псевдообратная матрица вместо обычной обратной -
# устойчивее, если ковариация плохо обусловлена
inv = np.linalg.pinv(cov)
# Квадрат расстояния Махаланобиса для каждой точки:
# (x - mu)^T inv (x - mu)
out[m] = np.einsum("bi,ij,bj->b", cen, inv, cen)
continue
# Изначально считаем, что сохраняем все точки класса
keep = np.ones(n, dtype=bool)
# Начальная грубая оценка среднего и обратной ковариации
mu = h.mean(axis=0, keepdims=True)
inv = np.linalg.pinv(np.cov(h.T) + lam * eye)
# Несколько итераций робастного trimming
for _ in range(max(1, iters)):
# Берём только текущие "надёжные" точки
hh = h[keep]
# Пересчитываем центр по оставшимся точкам
mu = hh.mean(axis=0, keepdims=True)
# Центрируем оставшиеся точки
cen_hh = hh - mu
# Пересчитываем ковариацию по очищенному подмножеству
cov = (cen_hh.T @ cen_hh) / max(1, len(hh) - 1) + lam * eye
inv = np.linalg.pinv(cov)
# Считаем расстояния Махаланобиса уже для всех точек класса
cen_all = h - mu
dist_all = np.einsum("bi,ij,bj->b", cen_all, inv, cen_all)
Страница 36 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
# Порог: оставляем (1 - trim_frac) долю ближайших точек
thr = float(np.quantile(dist_all, 1.0 - trim_frac))
keep = dist_all <= thr
# Защита от ситуации, когда осталось слишком мало точек:
# тогда ослабляем trimming
if keep.sum() < max(5, int(0.5 * n)):
keep = dist_all <= float(np.quantile(dist_all, 0.7))
# После финальной оценки считаем итоговый Mahalanobis score
# для всех точек этого класса
cen = h - mu
out[m] = np.einsum("bi,ij,bj->b", cen, inv, cen)
return out
def score_global_knn(emb, k=10):
"""
Считает глобальный kNN score:
среднее расстояние до k ближайших соседей по всему датасету.
Если точка находится изолированно от остальных,
её score будет большим.
"""
n = emb.shape[0]
# Если точек слишком мало, meaningful score нет
if n <= 2:
return np.zeros(n, dtype=np.float64)
# Нельзя брать соседей больше, чем число остальных точек
kk = min(k, n - 1)
x = emb.astype(np.float64)
# Квадраты норм для всех точек
x2 = np.sum(x * x, axis=1, keepdims=True)
# Матрица квадратов попарных евклидовых расстояний:
# ||xi - xj||^2 = ||xi||^2 + ||xj||^2 - 2 <xi, xj>
d2 = x2 + x2.T - 2.0 * (x @ x.T)
# Убираем расстояние точки до самой себя
np.fill_diagonal(d2, np.inf)
# Берём kk наименьших расстояний в каждой строке
knn = np.partition(d2, kk - 1, axis=1)[:, :kk]
Страница 37 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
# Возвращаем среднее обычное расстояние до ближайших соседей
return np.mean(np.sqrt(np.maximum(knn, 0.0)), axis=1)
def score_class_knn(emb, pred, k=8):
"""
Считает class-wise kNN score:
среднее расстояние до k ближайших соседей внутри предсказанного класса.
Это помогает находить объекты, которые нетипичны
именно для своего класса.
"""
out = np.zeros(len(emb), dtype=np.float64)
# Считаем score отдельно внутри каждого класса
for c in np.unique(pred):
m = pred == c
h = emb[m].astype(np.float64)
n = len(h)
# Если в классе слишком мало точек, score считаем нулевым
if n <= 2:
out[m] = 0.0
continue
kk = min(k, n - 1)
# Квадраты норм точек внутри класса
h2 = np.sum(h * h, axis=1, keepdims=True)
# Матрица квадратов попарных расстояний внутри класса
d2 = h2 + h2.T - 2.0 * (h @ h.T)
# Исключаем расстояние до самой себя
np.fill_diagonal(d2, np.inf)
# Находим kk ближайших соседей
knn = np.partition(d2, kk - 1, axis=1)[:, :kk]
# Среднее расстояние до ближайших соседей внутри класса
out[m] = np.mean(np.sqrt(np.maximum(knn, 0.0)), axis=1)
return out
def rank_score(x):
"""
Преобразует массив значений в ранги.
Наименьший элемент получает ранг 0,
следующий - 1, и так далее.
Страница 38 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
Это удобно, если потом нужно объединять
несколько score разного масштаба.
"""
# Индексы элементов в порядке возрастания значений x
order = np.argsort(x)
# Массив для рангов
r = np.empty_like(order, dtype=np.float64)
# Записываем для каждого элемента его позицию в отсортированном порядке
r[order] = np.arange(len(x), dtype=np.float64)
return r
[5]: # Строим outlier-score на тестовых эмбеддингах по ансамблю рангов
# из трёх метрик: robust Mahalanobis + global kNN + class kNN.
# Робастное расстояние Махаланобиса внутри предсказанного класса:
# показывает, насколько объект нетипичен относительно распределения своего
класса.
s_robust = score_robust_mahalanobis(emb_test, pred_test, lam=0.15,
trim_frac=0.10, iters=2)
# Глобальный kNN-score:
# среднее расстояние до ближайших соседей во всём наборе.
# Большие значения соответствуют более изолированным точкам.
s_gknn = score_global_knn(emb_test, k=10)
# Class-wise kNN-score:
# среднее расстояние до ближайших соседей только внутри своего предсказанного
класса.
# Помогает находить точки, которые странно выглядят именно внутри класса.
s_cknn = score_class_knn(emb_test, pred_test, k=8)
# Объединяем три score в один итоговый показатель подозрительности.
# Перед объединением переводим каждый score в ранги,
# чтобы разные шкалы значений не мешали друг другу.
# Здесь robust Mahalanobis имеет наибольший вес,
# global kNN - средний, class kNN - меньший дополнительный вклад.
final_score = (
2.0 * rank_score(s_robust)
+ 1.5 * rank_score(s_gknn)
+ 0.5 * rank_score(s_cknn)
)
# Выбираем K объектов с наибольшим итоговым score
# как наиболее вероятные выбросы.
topk = np.argsort(-final_score)[:K]
# Формируем бинарный вектор ответов:
# 1 - объект считаем выбросом, 0 - обычным объектом.
Страница 39 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
is_outlier = np.zeros(n_test, dtype=np.int64)
is_outlier[topk] = 1
# Собираем файл для отправки:
# для каждого id указываем предсказание is_outlier.
submission = pd.DataFrame({
"id": np.arange(n_test, dtype=np.int64),
"is_outlier": is_outlier,
})
# Сохраняем submission в CSV без индекса DataFrame.
submission.to_csv(SUBMISSION_PATH, index=False)
# Выводим служебную информацию:
# куда сохранён файл и сколько объектов помечено как выбросы.
print("Saved:", SUBMISSION_PATH)
print("Predicted outliers:", int(is_outlier.sum()))
# Показываем первые строки submission-таблицы.
submission.head()
Saved: submission.csv
Predicted outliers: 1000
[5]:
id is_outlier
0
0
0
1
1
0
2
2
0
3
3
0
4
4
0
Проверим решение: ячейка ниже вычисляет скор на public и private частях датасета.
[6]:
#!/usr/bin/env python3
"""
Validate submission.csv against y_test.csv and compute hits@k metrics.
Expected columns:
y_test.csv: id, is_outlier_true, Usage
submission.csv: id, is_outlier
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Dict, List
import pandas as pd
BASELINE_SOLUTION_SCORE_PUBLIC=113
Страница 40 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
BASELINE_SOLUTION_SCORE_PRIVATE=95
AUTHOR_SOLUTION_SCORE_PUBLIC=338
AUTHOR_SOLUTION_SCORE_PRIVATE=308
def _strip_jupyter_kernel_args(unknown: List[str]) -> List[str]:
cleaned: List[str] = []
i = 0
while i < len(unknown):
tok = unknown[i]
if tok == "-f" and i + 1 < len(unknown) and unknown[i + 1].
↪→
endswith(".json"):
i += 2
continue
cleaned.append(tok)
i += 1
return cleaned
def _validate_binary_column(col: pd.Series, name: str) -> pd.Series:
numeric = pd.to_numeric(col, errors="coerce")
bad = numeric.isna() | (~numeric.isin([0, 1]))
if bad.any():
first_bad_idx = int(col.index[bad][0])
raise ValueError(
f"Колонка '{name}' должна содержать только 0/1. "
f"Первая некорректная строка: {first_bad_idx}"
)
return numeric.astype(int)
def _clip_score_0_100(value: float) -> float:
return max(0.0, min(100.0, float(value)))
def _compute_hits_metrics(
df: pd.DataFrame, include_score_0_100: bool = True
) -> Dict[str, float | int | bool]:
k_true = int(df["is_outlier_true"].sum())
k_pred = int(df["is_outlier"].sum())
hits_at_k = int(((df["is_outlier_true"] == 1) & (df["is_outlier"] == 1)).
↪→
sum())
if AUTHOR_SOLUTION_SCORE <= BASELINE_SOLUTION_SCORE:
normalized_score_0_100 = 0.0
else:
normalized_score_0_100 = 100.0 * (
(hits_at_k - BASELINE_SOLUTION_SCORE)
/ (AUTHOR_SOLUTION_SCORE - BASELINE_SOLUTION_SCORE)
)
normalized_score_0_100 = _clip_score_0_100(normalized_score_0_100)
Страница 41 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
metrics: Dict[str, float | int | bool] = {
"n_samples": int(len(df)),
"k_true": k_true,
"k_pred": k_pred,
"k_match": bool(k_true == k_pred),
"hits_at_k": hits_at_k,
}
if include_score_0_100:
metrics["score_0_100"] = normalized_score_0_100
return metrics
def validate_and_score_submission(ytest_path: Path, submission_path: Path) ->
Dict[str, object]:
try:
ytest = pd.read_csv(ytest_path)
except Exception as exc: # pragma: no cover
raise ValueError(f"Ошибка чтения ytest.csv: {exc}") from exc
try:
submission = pd.read_csv(submission_path)
except Exception as exc: # pragma: no cover
raise ValueError(f"Ошибка чтения submission.csv: {exc}") from exc
if ytest.empty:
raise ValueError("ytest.csv пустой")
if submission.empty:
raise ValueError("submission.csv пустой")
required_ytest_cols = {"id", "is_outlier_true", "Usage"}
required_submission_cols = {"id", "is_outlier"}
missing_ytest_cols = required_ytest_cols - set(ytest.columns)
if missing_ytest_cols:
raise ValueError(f"В ytest.csv отсутствуют колонки:
{sorted(missing_ytest_cols)}")
missing_submission_cols = required_submission_cols - set(submission.
↪→
columns)
if missing_submission_cols:
raise ValueError(
f"В submission.csv отсутствуют колонки:
{sorted(missing_submission_cols)}"
)
ytest = ytest[["id", "is_outlier_true", "Usage"]].copy()
submission = submission[["id", "is_outlier"]].copy()
ytest["id"] = ytest["id"].astype(str).str.strip()
Страница 42 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
ytest["Usage"] = ytest["Usage"].astype(str).str.strip().str.title()
submission["id"] = submission["id"].astype(str).str.strip()
if (ytest["id"] == "").any():
raise ValueError("В ytest.csv есть пустые id")
if (submission["id"] == "").any():
raise ValueError("В submission.csv есть пустые id")
invalid_usage = sorted(set(ytest["Usage"]) - {"Public", "Private"})
if invalid_usage:
raise ValueError(f"В ytest.csv недопустимые значения Usage:
{invalid_usage}")
if ytest["id"].duplicated().any():
duplicates = (
ytest.loc[ytest["id"].duplicated(keep=False), "id"]
.unique()
.tolist()
)
suffix = " ..." if len(duplicates) > 10 else ""
raise ValueError(
f"В ytest.csv есть дубликаты id: {duplicates[:10]}{suffix} "
f"(всего {len(duplicates)})"
)
if submission["id"].duplicated().any():
duplicates = (
submission.loc[submission["id"].duplicated(keep=False), "id"]
.unique()
.tolist()
)
suffix = " ..." if len(duplicates) > 10 else ""
raise ValueError(
f"В submission.csv есть дубликаты id: {duplicates[:10]}{suffix} "
f"(всего {len(duplicates)})"
)
y_ids = set(ytest["id"])
s_ids = set(submission["id"])
missing_ids = y_ids - s_ids
if missing_ids:
miss = sorted(list(missing_ids))
suffix = " ..." if len(miss) > 10 else ""
raise ValueError(
f"В submission.csv отсутствуют id: {miss[:10]}{suffix} "
f"(всего {len(miss)})"
)
Страница 43 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
extra_ids = s_ids - y_ids
if extra_ids:
extra = sorted(list(extra_ids))
suffix = " ..." if len(extra) > 10 else ""
raise ValueError(
f"В submission.csv есть лишние id: {extra[:10]}{suffix} "
f"(всего {len(extra)})"
)
ytest["is_outlier_true"] =
_validate_binary_column(ytest["is_outlier_true"], "is_outlier_true")
submission["is_outlier"] =
_validate_binary_column(submission["is_outlier"], "is_outlier")
merged = ytest.merge(submission, on="id", how="left",
validate="one_to_one")
if merged["is_outlier"].isna().any():
raise ValueError("После merge есть NaN в предсказаниях")
k_true_total = int(merged["is_outlier_true"].sum())
k_pred_total = int(merged["is_outlier"].sum())
if k_pred_total != k_true_total:
raise ValueError(
"В submission.csv должно быть ровно K единиц в колонке
is_outlier, "
f"где K={k_true_total}. Сейчас: {k_pred_total}"
)
overall_metrics = _compute_hits_metrics(merged, include_score_0_100=True)
public_metrics = _compute_hits_metrics(
merged.loc[merged["Usage"] == "Public"], include_score_0_100=False
)
private_metrics = _compute_hits_metrics(
merged.loc[merged["Usage"] == "Private"], include_score_0_100=False
)
overall_metrics["score_0_100"] =
_clip_score_0_100(overall_metrics["score_0_100"])
return {
**overall_metrics,
"n_total": int(len(merged)),
"n_public": int((merged["Usage"] == "Public").sum()),
"n_private": int((merged["Usage"] == "Private").sum()),
"public": public_metrics,
"private": private_metrics,
}
def main(argv: List[str] | None = None) -> None:
Страница 44 из 45
Заключительный этап всероссийской олимпиады школьников по информатике.
Профиль «Искусственный интеллект». Второй тур. Москва, 25 марта 2026 г.
parser = argparse.ArgumentParser(description="Проверка сабмита и подсчет
hits@k")
parser.add_argument("--ytest", type=str, default="y_test.csv", help="Путь
к y_test.csv")
parser.add_argument(
"--submission",
type=str,
default="submission.csv",
help="Путь к submission.csv",
)
parser.add_argument(
"--save-json",
type=str,
default=None,
help="Опциональный путь для сохранения метрик в JSON",
)
args, unknown = parser.parse_known_args(argv)
unknown = _strip_jupyter_kernel_args(unknown)
if unknown:
parser.error(f"unrecognized arguments: {' '.join(unknown)}")
ytest_path = Path(args.ytest)
submission_path = Path(args.submission)
if not ytest_path.exists():
raise FileNotFoundError(f"файл ytest не найден: {ytest_path}")
if not submission_path.exists():
raise FileNotFoundError(f"файл submission не найден:
{submission_path}")
metrics = validate_and_score_submission(ytest_path, submission_path)
print(json.dumps(metrics, indent=2))
if args.save_json is not None:
out_path = Path(args.save_json)
with out_path.open("w", encoding="utf-8") as file:
json.dump(metrics, file, indent=2)
print(f"Метрики сохранены в JSON: {out_path}")
if __name__ == "__main__":
main()
Страница 45 из 45

 

 

 

 

 

 

 

 

 

 

//////////////////////////////////////////