Школьный этап, 9–11 классы всероссийской олимпиады по информатике: 5 задач с официальным разбором, к каждой наше решение на C++.
К задаче 1Школьный этап ВсОШ по информатике 2023/24, 9–11 классы.
Задачи тура
- 1Пара-тройка конфетматематика
- 2Речной бойматематика, игры
- 3Красивый шарфматематика
- 4Гостиница для жирафовдвоичная система, жадный алгоритм
- 5Сломанный индикатормножества, перебор
Отмечайте решённые: 0 из 5
1. Пара-тройка конфет
У Алисы сегодня день рождения, и она хочет угостить своих одноклассников конфетами. В магазине, в который она успеет зайти перед школой, есть сладости двух видов: шоколадные и карамельные. Они продаются наборами по 3 штуки, причем в упаковке есть конфеты каждого из двух видов (то есть в одной упаковке лежат две конфеты одного вида и одна конфета другого вида). По внешнему виду упаковки нельзя понять, какие конфеты лежат внутри.
Чтобы никого не обидеть, всем в классе нужно раздать конфеты одного вида, а оставшиеся девочка заберёт домой. Алисе нужно собираться в школу, поэтому она попросила вас посчитать, какое минимальное число упаковок нужно купить, чтобы конфет хватило на всех.
Формат входных данных
В единственной строке задано число () — количество человек в классе.
Формат выходных данных
Выведите единственное число — количество упаковок, которое должна купить Алиса.
Система оценки
Решения, правильно работающие при , будут оцениваться в 25 баллов.
Решения, правильно работающие при , будут оцениваться в 50 баллов.
Замечание
В первом примере Алиса купит две упаковки с конфетами. В первой упаковке лежат 2 конфеты одного вида, и 1 конфета другого вида. Если вторая упаковка будет такая же, как и первая, то у Алисы окажется 4 конфеты одного вида и 2 конфеты другого вида. Если вторая упаковка будет отличаться от первой, то у Алисы будет по 3 конфеты каждого вида. В любом случае у Алисы найдётся 3 конфеты одного вида.
Как видно из первого примера, для того, чтобы гарантированно получить 4 конфеты одного вида, недостаточно купить две упаковки.
| стандартный ввод | стандартный вывод |
|---|---|
3 | 2 |
| стандартный ввод | стандартный вывод |
|---|---|
4 | 3 |
Официальный разборСначала попробуйте решить сами
Наше решение на C++Полный код на C++17
Официальный разбор
Заметим, что упаковки конфет могут быть двух типов: с двумя шоколадными + одной карамельной или одной шоколадной + двумя карамельными. Также заметим, что ответ не превосходит , так как в каждой упаковке есть хотя бы одна конфета каждого вида.
При можно перебрать количество купленных коробок и убедиться, что при любом распределении конфет в купленных упаковках найдётся не менее конфет одного вида.
Пусть — количество купленных коробок, из них — первого типа (две шоколадные и одна карамельная), тогда — число коробок второго типа (одна шоколадная и две карамельные). Конфет одного вида будет . Для выбранного переберём значения такие, что и если для всех верно, что , то данное значение подходит. Минимальное подходящее является ответом.
Такое решение имеет сложность и набирает 25 баллов. Пример такого решения.
n = int(input())
k = 1
ok = False
while not ok:
ok = True
for k1 in range(k + 1):
k2 = k - k1
if max(2 * k1 + k2,
k1 + 2 * k2) < n:
ok = False
k += 1
print(k - 1)При всё ещё получится перебирать значения , но необходимо избавиться от вложенного цикла, перебирающего возможное распределение упаковок. Заметим, что в худшем случае (чтобы минимизировать количество конфет одного вида), значение , то есть конфет каждого вида окажется примерно поровну.
Заметим, что в двух упаковках точно найдётся три конфеты одного вида. Если мы покупаем чётное число упаковок, то среди них точно будет конфет одного вида. Если нечетное, то количество конфет одного вида будет не меньше, чем . Переберём значения , пока не найдём минимальное подходящее.
Такое решение имеет сложность и набирает 50 баллов. Пример такого решения.
n = int(input())
k = 0
while k // 2 * 3 + (k % 2) * 2 < n:
k += 1
print(k)Полное решение имеет сложность . Если делится на 3, то нужно купить упаковок (в каждых двух упаковках есть три конфеты одного вида). Если же купить на одну упаковку больше, то в лишней упаковке окажутся 2 одинаковые конфеты. Поэтому для тех значений , которые не делятся на 3, нужно будет купить ещё одну дополнительную упаковку.
Пример такого решения.
n = int(input())
if n % 3 == 0:
print(n // 3 * 2)
else:
print(n // 3 * 2 + 1)Наше решение на C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long n;
cin >> n;
if (n % 3 == 0) cout << n / 3 * 2 << "\n";
else cout << n / 3 * 2 + 1 << "\n";
return 0;
}2. Речной бой
Поле в игре «Речной бой» представляет собой полоску длины клеток и шириной в одну клетку. Где-то на поле расположен корабль из клеток (). Какое наименьшее число выстрелов необходимо, чтобы гарантированно потопить корабль? После каждого выстрела сообщается его результат: «мимо», «ранен» или «убит».
Формат входных данных
Первая строка входных данных содержит целое число ().
Вторая строка входных данных содержит целое число ().
Формат выходных данных
Выведите одно целое число — количество выстрелов.
Система оценки
Решения, правильно работающие при , будут оцениваться в 40 баллов.
Замечание
В первом примере поле состоит из клеток, корабль имеет длину . Первый выстрел нужно сделать в одну из двух центральных клеток. Если результатом будет «ранен», то вторая клетка корабля находится в одной из двух соседних клеток, и за два выстрела мы гарантированно потопим корабль Если результатом первого выстрела будет «мимо», то корабль занимает две единственные свободные смежные клетки, которые тоже можно подбить двумя выстрелами. Итого нужно 3 выстрела. Двух выстрелов недостаточно, так как всегда есть шанс промахнуться первым выстрелом.
| стандартный ввод | стандартный вывод |
|---|---|
4 2 | 3 |
| стандартный ввод | стандартный вывод |
|---|---|
4 4 | 4 |
Официальный разборСначала попробуйте решить сами
Наше решение на C++Полный код на C++17
Официальный разбор
Будем стрелять последовательно так, чтобы не пропустить корабль. Для этого нужно стрелять, оставляя между выстрелами пустую клетку, то есть в клетки с номерами и т.д., до тех пор, пока не получим результат «ранен», либо такие клетки не кончатся. Мы сделаем не более, чем выстрелов (целочисленное частное от деления на ). Получив на каком-то выстреле ответ «ранен», начнём стрелять в соседние клетки в одну сторону, а при получении результата «мимо» — в другую сторону. Тем самым мы сделаем не более одного дополнительного промаха, то есть для попадания в клетку корабля понадобятся не более выстрелов, и общее число выстрелов будет равно .
Но если делится на , то последний выстрел из первой последовательности придётся в последнюю клетку поля, и тогда с одной стороны от этой клетки больше не останется клеток. В этом случае достаточно только дополнительных выстрелов, то есть при , делящемся на , ответ равен .
Используя целочисленное деление, оба этих случая можно записать одной формулой.
n = int(input())
k = int(input())
print((n - 1) // k + k)Наше решение на C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long n, k;
cin >> n >> k;
cout << (n - 1) / k + k << "\n";
return 0;
}3. Красивый шарф
Алиса решила поздравить своего друга с началом нового учебного года. Впереди холодная осень, поэтому она решила связать для него собственными руками шарф.
Незаметно для друга Алиса узнала, что ему большего всего нравятся различных цветов. Алиса приняла решение связать шарф размером , в котором будут чередоваться полоски различных цветов. Её друг никогда не ищет легких путей, поэтому она решила, что шарф с горизонтальными или вертикальными полосками покажется ему слишком «примитивным». Алиса решила, что полоски определённо должны быть диагональными!
Закончив вязать шарф, Алиса вспомнила, что один из цветов её друг считает особенным! Это цвет , который по его мнению приносит школьникам удачу на олимпиадах по информатике. И Алисе стало невероятно интересно, сколько фрагментов шарфа имеют именно такой цвет. Шарф получился очень большим, Алиса очень устала, пока его вязала, поэтому сама она уже не может ответить на этот вопрос и просит вас о помощи...
Более формально шарф можно представить в виде таблицы размером , каждая клетка которой покрашена в один из цветов. Цвета нумеруются от до .
Первая строка таблицы покрашена в цвета и т.д. Каждая следующая строка получена из предыдущей сдвигом влево на одну клетку. Таким образом, таблица состоит из диагональных полос.
При , и таблица будет иметь следующий вид:

По данным числам , , и определите, сколько всего клеток покрашено в цвет .
Формат входных данных
Первая строка входных данных содержит натуральное число — ширину шарфа.
Вторая строка входных данных содержит натуральное число — длину шарфа.
Третья строка входных данных содержит натуральное число — количество любимых цветов друга Алисы.
Числа , и не превосходят .
Четвёртая строка входных данных содержит натуральное число — номер особенного цвета ().
Формат выходных данных
Программа должна вывести одно целое число — количество клеток шарфа, которые покрашены в цвет .
Обратите внимание, что ответ в этой задаче может превышать возможное значение 32-битной целочисленной переменной, поэтому необходимо использовать 64-битные целочисленные типы данных (тип int64 в языке Pascal, тип long long в C++, тип long в Java и C#).
Система оценки
Решения, правильно работающие, когда число не превосходит , будут оцениваться в 16 баллов.
Решения, правильно работающие, когда одно из чисел или делится нацело на , будут оцениваться в 16 баллов.
Решения, правильно работающие, когда числа и не превосходят , будут оцениваться в 40 баллов.
Решения, правильно работающие, когда число не превосходит , будут оцениваться в 56 баллов.
Решения, правильно работающие, когда числа и не превосходят , будут оцениваться в 60 баллов.
Решения, правильно работающие, когда число не превосходит , будут оцениваться в 84 балла.
Замечание
Картинка соответствует примеру из условия. Шарф имеет размеры и состоит из клеток трёх цветов. В цвет 1 покрашены 11 клеток.
| стандартный ввод | стандартный вывод |
|---|---|
4 8 3 1 | 11 |
Официальный разборСначала попробуйте решить сами
Наше решение на C++Полный код на C++17
Официальный разбор
В решении на 40 баллов можно явно построить требуемую таблицу. На самом деле не требуется сохранять всю таблицу в массиве, достаточно перебрать номера строк и столбцов. Клетка будет иметь цвет , если нумеровать строки и столбцы с нуля. Вычислим цвет клетки и если он равен , увеличим ответ на 1. Такое решение имеет сложность .
n = int(input())
m = int(input())
k = int(input())
c = int(input())
ans = 0
for i in range(n):
for j in range(m):
if (i + j) % k + 1 == c:
ans += 1
print(ans)В решении на 60 баллов нужно убрать вложенные циклы и перебирать только строки или только столбцы. Вычислим для первой строки цвет первой клетки в столбце (это значение запишем в переменную first) и цвет последней клетки (это значение запишем в переменную last), а также количество клеток цвета в строке, которое будем хранить в переменной count. Значения first и last при переходе к следующей строке увеличиваются на 1 c учётом зацикливания при достижении значения . Также будем обновлять значение count в зависимости от first и last — если в какой-то строке значение first было равно , то при переходе к следующей строке этот цвет «сдвинется» влево, и count уменьшится на 1. Аналогично, count увеличивается на 1, когда значение last становится равно .
Такое решение имеет сложность . Его удобнее реализовать, если нумеровать цвета с нуля, тогда обновление переменных first и last при переходе к следующей строке будет выполняться присваиваниями first = (first + 1) % k, last = (last + 1) % k.
n = int(input())
m = int(input())
k = int(input())
c = int(input()) - 1
answer = 0
first = 0
last = (m - 1) % k
count = 0
for j in range(m):
if j % k == c:
count += 1
for i in range(n):
answer += count
if first == c:
count -= 1
first = (first + 1) % k
last = (last + 1) % k
if last == c:
count += 1
print(answer)Полное решение имеет сложность . Покрашенную в цвет клетку станем просто называть покрашенной, а остальные клетки — непокрашенными. Нам необходимо найти количество покрашенных клеток. Заметим, что среди подряд идущих клеток в одной строке или в одном столбце будет ровно одна покрашенная. Это позволяет отбросить целиком любые строк: в одной группе из строк окажется ровно покрашенных клеток. Таких групп , после чего мы сможем заменить на остаток от деления на . Сделаем то же самое со столбцами: каждая группа из столбцов будет иметь покрашенных клеток.
Теперь нам нужно посчитать ответ только для прямоугольника , стороны которого меньше . Пройдём по строкам и определим количество покрашенных клеток в каждой строке. Оно может быть равно 0 или 1, поэтому просто посчитаем количество строк, в которых такая клетка есть. Например, если в квадрате такая клетка была в первой строке (что возможно в случае ), то таких строк будет — первоначально эта клетка была в столбце , а затем начнёт сдвигаться влево, пока не дойдёт до первого столбца или пока не кончатся строки. Если же , то посчитаем, когда эта клетка «войдёт» в последний столбец, для этого нужно будет пропустить столбцов.
Ещё нужно учесть случай, когда покрашенная клетка сдвинется налево и покинет строку квадрата, а потом снова «войдёт» с правого столбца.
Пример такого решения.
n = int(input())
m = int(input())
k = int(input())
c = int(input())
ans = 0
ans += (n // k) * m
n %= k
ans += (m // k) * n
m %= k
if c > m:
n -= c - m
c = m
if n > 0:
ans += min(c, n)
c += k
if c > m:
n -= c - m
c = m
if n > 0:
ans += min(c, n)
print(ans)Наше решение на C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long n, m, k, c;
cin >> n >> m >> k >> c;
long long answer = 0;
answer += (n / k) * m;
n %= k;
answer += (m / k) * n;
m %= k;
for (int pass = 0; pass < 2; pass++) {
if (c > m) {
n -= c - m;
c = m;
}
if (n > 0) answer += min(c, n);
c += k;
}
cout << answer << "\n";
return 0;
}4. Гостиница для жирафов
В гостинице для жирафов администрация хочет запастись подушкам так, чтобы удовлетворить потребности любого своего возможного постояльца. Известно, что жирафам в зависимости от длины их шеи нужно сложить стопку подушек (в стопке одна или несколько подушек) толщиной от до сантиметров. При этом администрация хочет обойтись как можно меньшим числом подушек, а среди наборов подушек, удовлетворяющих этим требованиям, администрация выберет набор минимальной суммарной толщины, чтобы он занимал минимальный объём в шкафу.
Помогите администрации составить нужный набор подушек, позволяющий получить стопку любой высоты от до сантиметров включительно.
Формат входных данных
Во входных данных записано единственное целое число — максимально возможная длина шеи жирафа ().
Формат выходных данных
В единственной строке через пробел выведите толщину каждой подушки в этом наборе в произвольном порядке. Если ответов несколько, выведите любой из них.
Система оценки
Решения, правильно работающие при , будут оцениваться в 20 баллов.
Решения, правильно работающие при , будут оцениваться в 40 баллов.
Замечание
В примере из условия необходимо подобрать такой набор из минимального числа подушек, чтобы используя данные подушки удавалось сложить стопку любой целочисленной толщины от 1 до 9 см. Таким набором является набор из подушек толщиной 1, 2, 3, 3 см. Действительно, стопку толщины 1, 2, 3 см можно сложить из одной подушки. Оставшиеся числа получены так: , , , , , . Возможны и другие варианты ответа с тем же количеством подушек и их суммарной толщиной. Выполнить условие задачи, используя только три подушки, нельзя.
| стандартный ввод | стандартный вывод |
|---|---|
9 | 1 2 3 3 |
Официальный разборСначала попробуйте решить сами
Наше решение на C++Полный код на C++17
Официальный разбор
Пусть имеется набор из подушек. Всего существует всевозможных подмножеств этого набора, не считая пустого. Поэтому различных вариантов толщины набора будет не более . Поэтому, если некоторый набор из подушек позволит сформировать все стопки толщиной от до , то
откуда
где — операция округления числа «вверх».
Предъявить подходящий набор, содержащий в точности подушек, несложно: для этого достаточно рассмотреть степени двойки для всех от до . Представление всех чисел от до в виде сумм таких чисел соответствует двоичному представлению этих чисел, поэтому такой набор является достаточным для получения всех нужных сумм и минимальным по количеству используемых подушек. Однако суммарная толщина всех подушек может оказаться не минимально возможной: сумма чисел в этом наборе равна , что может оказаться больше , поэтому в таком наборе присутствует некоторая избыточность с точки зрения величины получаемых сумм.
Важно, что нам не нужно получать суммы, большие . Поэтому давайте в рассматриваемом наборе заменим подушку самой большой толщины на подушку толщины
то есть — разница между и суммарной толщиной всех остальных подушек в наборе. Нетрудно понять, что такой набор позволяет представить все суммы от до : действительно, суммы от до можно получить без использования подушки толщины , а все большие суммы вплоть до — прибавляя подушку толщины к некоторой меньшей сумме. Этот набор, как показано ранее, содержит минимальное достаточное количество подушек. Кроме того, суммарная толщина всех подушек в наборе равна — меньше суммарная толщина в подходящем наборе быть не может, потому что иначе таким набором нельзя получить толщину . Таким образом, мы получили полностью удовлетворяющий условию задачи набор из подушек:
Для реализации этого решения будем просто брать последовательные степени двойки, уменьшая значение и добавляя к ответу в конце оставшееся значение , если оно оказалось ненулевым.
n = int(input())
last = 1
while last <= n:
print(last)
n -= last
last *= 2
if n > 0:
print(n)Это не единственный возможный ответ, что показывает, в частности, пример для : подходящими наборами являются , , и .
Наше решение на C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long n;
cin >> n;
vector<long long> pillows;
long long power = 1;
while (power <= n) {
pillows.push_back(power);
n -= power;
power *= 2;
}
if (n > 0) pillows.push_back(n);
for (size_t i = 0; i < pillows.size(); i++) {
if (i) cout << " ";
cout << pillows[i];
}
cout << "\n";
return 0;
}5. Сломанный индикатор
У радиолюбителя Алексея есть девятисегментный жидкокристаллический индикатор, который может показывать цифры от 0 до 9 в виде цифр «почтового индекса» (см. рисунок):

После неудачного эксперимента индикатор повредился, и часть сегментов могла перегореть. Когда сегмент перегорает, индикатор теряет возможность показывать цифры, использующие этот сегмент.
Алексей уже выяснил, что индикатор всё ещё способен показать какие-то цифр. Однако радиолюбитель не может проверить остальные цифры, равно как и каждый сегмент отдельно. Поэтому он просит вас помочь найти те цифры, которые гарантированно можно показать на этом индикаторе.
Формат входных данных
Первая строка входных данных содержит число () — количество цифр, которые смог показать на индикаторе Алексей.
Следующие строк содержат по одной цифре () — сами цифры, которые Алексей смог показать. Гарантируется, что все различны.
Формат выходных данных
Выведите элементы искомого множества в порядке возрастания, каждую цифру в отдельной строке.
Система оценки
Решения, правильно работающие при , будут оцениваться в 28 баллов.
| стандартный ввод | стандартный вывод |
|---|---|
3 2 4 8 | 0 2 4 5 8 9 |
| стандартный ввод | стандартный вывод |
|---|---|
2 6 1 | 1 6 |
Официальный разборСначала попробуйте решить сами
Наше решение на C++Полный код на C++17
Официальный разбор
Для начала формализуем условие задачи. Некий индикатор состоит из 9 сегментов, каждый из которых либо гарантированно работает (зажигается какой-то цифрой из данного набора), либо имеет неизвестное состояние (не зажигается ни одной из данных цифр). Задача заключается в нахождении всех цифр, которые состоят только из гарантированно рабочих индикаторов.
Пронумеруем сегменты индикатора сверху вниз, слева направо, то есть самый верхний сегмент будет иметь номер 1, а самый нижний — 9. Для каждой возможной цифры перечислим сегменты, из которой она состоит. Например, цифра 0 состоит из сегментов 1, 2, 4, 6, 8, 9.
Если какая-то цифра могла быть отображена на экране, то соответствующие ей сегменты заведомо исправны. Если несколько цифр могли быть отображены на экране, то исправны сегменты, которые входят в хотя бы одну данную цифру. То есть нужно сделать объединение множеств сегментов, соответствующих данным цифрам. Это удобно реализовать при помощи структуры данных «множество» в языке Python, но можно использовать и массивы из нулей и единиц (где единица будет соответствовать исправному сегменту индикатора) или битовые операции, но тогда реализация станет сложнее.
Для проверки, что какую-то цифру гарантированно удастся показать на индикаторе, множество сегментов этой цифры должно быть подмножеством заведомо исправных сегментов. Для этого в Python есть операция <= для множеств.
Приведём пример решения на языке Python. Здесь segments — это список множеств, соответствующих цифрам, то есть segments[i] — это список сегментов, входящих в цифру .
После нахождения объединения множеств сегментов переберём все цифры от 0 до 9 и выведем подходящие из них.
segments = [{1, 2, 4, 6, 8, 9}, {3, 4, 8}, {1, 4, 7, 9}, {1, 3, 5, 7}, {2, 4, 5, 8},
{1, 2, 5, 8, 9}, {3, 5, 6, 8, 9}, {1, 3, 6}, {1, 2, 4, 5, 6, 8, 9}, {1, 2, 4, 5, 7}]
res = set()
n = int(input())
for i in range(n):
d = int(input())
res |= segments[d]
for d in range(0, 10):
if segments[d] <= res:
print(d)Для получения частичного балла при достаточно было вручную исследовать, какие ответы возникают на 7 возможных тестах этой группы, занести их в программу и выводить нужный ответ в зависимости от ввода.
Наше решение на C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
vector<vector<int>> segments = {
{1, 2, 4, 6, 8, 9}, {3, 4, 8}, {1, 4, 7, 9}, {1, 3, 5, 7}, {2, 4, 5, 8},
{1, 2, 5, 8, 9}, {3, 5, 6, 8, 9}, {1, 3, 6}, {1, 2, 4, 5, 6, 8, 9}, {1, 2, 4, 5, 7}};
int n;
cin >> n;
vector<bool> working(10, false);
for (int i = 0; i < n; i++) {
int digit;
cin >> digit;
for (int s : segments[digit]) working[s] = true;
}
for (int digit = 0; digit <= 9; digit++) {
bool ok = true;
for (int s : segments[digit]) {
if (!working[s]) ok = false;
}
if (ok) cout << digit << "\n";
}
return 0;
}Сначала решите сами
Возьмите столько времени, сколько было на олимпиаде, и только потом открывайте разбор.
Другие туры
2026/27
- Школьный этап, 9–11 классы5 задач
- Школьный этап, 7–8 классы6 задач
- Школьный этап, 5–6 классы5 задач
2025/26
- Школьный этап, 9–11 классы5 задач
- Школьный этап, 7–8 классы6 задач
- Школьный этап, 5–6 классы5 задач
- Муниципальный этап, 9–11 классы5 задач
- Муниципальный этап, 7–8 классы6 задач
2024/25
- Школьный этап, 9–11 классы5 задач
- Школьный этап, 7–8 классы7 задач
- Школьный этап, 5–6 классы5 задач
- Муниципальный этап, 9–11 классы5 задач
- Муниципальный этап, 7–8 классы7 задач
2023/24
- Школьный этап, 9–11 классы5 задач
- Школьный этап, 7–8 классы7 задач
- Школьный этап, 5–6 классы5 задач
- Муниципальный этап, 9–11 классы5 задач
- Муниципальный этап, 7–8 классы7 задач