ЕГЭ · Информатика · Массивы и строки
Списки и обработка последовательностей
Списки, срезы и однопроходная обработка — фундамент задания 17.
🎯 ЕГЭ информатика: задания 8, 17, 24, 26, 27. №8 — комбинаторика: подсчёт слов и чисел; №17 — обработка последовательности чисел (файл); №24 — обработка символьной информации (файл); №26 — обработка данных из файла: рейтинг; №27 — анализ данных.
- ⚠Читают файл не полностью или неверно разбирают строки в числа
- ⚠Забывают дополнительное условие (кратность, пара соседних, диапазон)
| Индексация | с нуля: a[0] первый, a[-1] последний |
|---|---|
| Срезы | a[1:3] — со 2-го по 3-й; a[i:i+2] — пара соседних |
| Функции | len, sum, max, min, a.count(x) |
Соседние пары перебирают так: for i in range(len(a)-1): x,y = a[i], a[i+1]. Индекс i+1 должен существовать — цикл идёт до len(a)−1. Для задания 17 часто нужны два прохода: сначала глобальная величина, потом сбор ответа.
Разбор примера
Обработка соседних пар
Посчитать количество пар соседних элементов, дающих чётную сумму.
Показать решение по шагам
- 1.
a = [3, 1, 4, 1, 5, 2] count = 0 for i in range(len(a) - 1): if (a[i] + a[i+1]) % 2 == 0: count += 1 print(count)
Ответ: Считаются пары с чётной суммой за один проход
Вопрос на проверку
Что вернёт a[-1] для списка a = [10, 20, 30]?
Ответить и проверить себя — после бесплатной регистрации.
Вопрос на проверку
Как получить минимум списка a в Python?
Ответить и проверить себя — после бесплатной регистрации.
Линия 17 — одно задание с пятьюдесятью лицами
Если посмотреть все задания линии 17 в банке ФИПИ подряд, обнаруживается удивительная вещь: формулировка у них одна, и меняется в ней ровно одно придаточное. Вот реальные концовки условий:
— «определите количество пар последовательности, оба элемента которых — нечётные числа, и максимальную из сумм элементов таких пар»; — «…сумма элементов которых делится на 9 без остатка…»; — «…модуль разности элементов которых делится на 13 без остатка…»; — «…ровно один элемент которых — чётное число…»; — «…оба элемента которых — четырёхзначные числа…»; — «…оба элемента которых больше среднего арифметического всех элементов последовательности…».
И всегда одно и то же уточнение: «Под парой подразумеваются два идущих подряд элемента последовательности».
Значит, учить надо не пятьдесят решений, а один шаблон и способ вставить в него условие. Ответ тоже всегда одинаков по форме: сначала количество подходящих пар, потом наибольшая из их сумм.
Отдельно стоит вариант со средним арифметическим: он единственный, где одного прохода не хватает, и разобран ниже отдельно.
Универсальный шаблон линии 17
Программа состоит из четырёх частей, и три из них не меняются никогда.
Чтение файла. a = [int(x) for x in open('17.txt')]. В файле числа записаны по одному в строке, и int сам отбрасывает перевод строки, так что strip() не нужен.
Перебор соседних пар. for i in range(len(a) - 1), внутри x, y = a[i], a[i + 1]. Граница len(a) - 1 обязательна: при range(len(a)) на последнем шаге обращение к a[i + 1] вылетит за конец списка. Пар всегда ровно на одну меньше, чем элементов.
Два накопителя. kolichestvo = 0 и maksimum = 0. Второй лучше заводить нулём, а не None: суммы в линии 17 положительные, и нуль никогда не окажется больше настоящей суммы. Если бы элементы могли быть отрицательными, надёжнее было бы maksimum = None с проверкой.
Условие. Единственная строка, которая меняется от варианта к варианту.
a = [int(x) for x in open('17.txt')]
kolichestvo = 0
maksimum = 0
for i in range(len(a) - 1):
x, y = a[i], a[i + 1]
if x % 2 == 1 and y % 2 == 1: # ← вот эта строка
kolichestvo += 1
if x + y > maksimum:
maksimum = x + y
print(kolichestvo, maksimum)
Восемь строк, из которых думать надо над одной. Выучите каркас так, чтобы он набирался механически, и всё внимание останется на условии.
Разбор примера
Линия 17: полная трассировка по всем девяти парам
Дана последовательность: 12, 7, 9, 24, 15, 3, 8, 21, 5, 40. Определите количество пар, оба элемента которых — нечётные числа, и максимальную из сумм элементов таких пар.
Показать решение по шагам
- 1. Элементов десять, значит соседних пар девять: (12,7), (7,9), (9,24), (24,15), (15,3), (3,8), (8,21), (21,5), (5,40). Проверим их по очереди. Начальное состояние: kolichestvo = 0, maksimum = 0.
- 2. i = 0: пара (12, 7). 12 чётное → условие не выполнено. Счётчики без изменений.
- 3. i = 1: пара (7, 9). Оба нечётные ✔ kolichestvo = 1. Сумма 16, она больше нуля → maksimum = 16.
- 4. i = 2: пара (9, 24). 24 чётное → мимо.
- 5. i = 3: пара (24, 15). 24 чётное → мимо. Обратите внимание: девятка и пятнадцатка сами по себе нечётные, но в паре с чётным соседом они не считаются — условие про пару целиком.
- 6. i = 4: пара (15, 3). Оба нечётные ✔ kolichestvo = 2. Сумма 18 > 16 → maksimum = 18.
- 7. i = 5: пара (3, 8). 8 чётное → мимо.
- 8. i = 6: пара (8, 21). 8 чётное → мимо.
- 9. i = 7: пара (21, 5). Оба нечётные ✔ kolichestvo = 3. Сумма 26 > 18 → maksimum = 26.
- 10. i = 8: пара (5, 40). 40 чётное → мимо. Цикл закончен.
- 11. Ответ: 3 26. Три пары, наибольшая сумма 26.
- 12. Обратите внимание на элемент 15: он входит в две пары — (24,15) и (15,3). Это нормально и предусмотрено условием: пары перекрываются, каждый элемент, кроме крайних, участвует дважды. Попытка «разбить последовательность на непересекающиеся пары» — грубая ошибка, которая уменьшает ответ примерно вдвое.
Ответ: 3 26
| оба элемента нечётные | x % 2 == 1 and y % 2 == 1 |
|---|---|
| оба элемента чётные | x % 2 == 0 and y % 2 == 0 |
| ровно один элемент чётный | (x % 2 == 0) != (y % 2 == 0) — исключающее «или», а не обычное |
| сумма делится на 9 | (x + y) % 9 == 0 |
| модуль разности делится на 13 | abs(x - y) % 13 == 0 |
| оба четырёхзначные | 1000 <= x <= 9999 and 1000 <= y <= 9999 |
| ровно один оканчивается на 5 | (x % 10 == 5) != (y % 10 == 5) |
| оба больше среднего | x > sredneye and y > sredneye — требует двух проходов |
«Ровно один» — это исключающее ИЛИ
Формулировка «ровно один элемент которых — чётное число» ломает больше решений, чем все остальные вместе. Соблазн написать x % 2 == 0 or y % 2 == 0 велик, но это неверно: обычное «или» в Python включающее, и пара из двух чётных чисел тоже его удовлетворит.
Правильных записей три, и все годятся.
Через неравенство логических значений. (x % 2 == 0) != (y % 2 == 0). Каждая скобка даёт True или False; они не равны ровно тогда, когда одно из чисел чётное, а другое нет.
Через счёт. [x % 2, y % 2].count(0) == 1 — среди двух остатков ровно один нулевой.
Через чётность суммы. Сумма чётного и нечётного всегда нечётна, а сумма двух одинаковых по чётности — чётна. Значит, (x + y) % 2 == 1 — самая короткая запись того же условия. Она работает только для признака чётности; для «ровно один оканчивается на 5» так сократить не выйдет, там нужен первый способ.
Проверьте себя на паре (8, 21): чётное и нечётное, «ровно один чётный» выполнено. На паре (12, 24): оба чётные, условие не выполнено. На паре (7, 9): оба нечётные, тоже не выполнено.
Когда одного прохода не хватает
Вариант «оба элемента больше среднего арифметического всех элементов последовательности» устроен иначе, и в нём теряют балл чаще всего.
Причина в слове «всех». Среднее зависит от всей последовательности, включая числа, до которых цикл ещё не дошёл. Значит, считать среднее «на лету» в том же проходе, где идёт отбор, нельзя: на первых шагах оно будет посчитано по неполным данным и окажется неверным.
Правильный порядок — два прохода:
Первый проход (или просто sum(a) / len(a)) даёт среднее по всей последовательности.
Второй проход идёт по парам и сравнивает элементы с уже известным числом.
Вторая тонкость — деление. sum(a) / len(a) даёт дробное число, и это правильно: округлять среднее нельзя. Если написать sum(a) // len(a), целочисленное деление отбросит дробную часть, и элементы, попадающие между настоящим средним и округлённым, будут отнесены не в ту сторону. На средних вроде 14,4 это меняет ответ.
Та же логика работает во всех формулировках со словами «всех», «максимального», «наименьшего»: величина, вычисляемая по всей последовательности, требует отдельного прохода до начала отбора.
Разбор примера
Линия 17: вариант со средним арифметическим
Та же последовательность 12, 7, 9, 24, 15, 3, 8, 21, 5, 40. Определите количество пар, оба элемента которых больше среднего арифметического всех элементов, и максимальную из сумм таких пар.
Показать решение по шагам
- 1.
a = [int(x) for x in open('17.txt')]
sredneye = sum(a) / len(a) # первый проход
kolichestvo = 0 maksimum = 0 for i in range(len(a) - 1): # второй проходx, y = a[i], a[i + 1]
if x > sredneye and y > sredneye: kolichestvo += 1 if x + y > maksimum: maksimum = x + yprint(kolichestvo, maksimum)
- 2. Шаг 1. Первый проход. Сумма: 12 + 7 + 9 + 24 + 15 + 3 + 8 + 21 + 5 + 40 = 144. Элементов десять, значит среднее равно 144 / 10 = 14,4.
- 3. Шаг 2. Отметим, какие элементы больше 14,4: это 24, 15, 21 и 40. Все остальные — 12, 7, 9, 3, 8, 5 — меньше.
- 4. Шаг 3. Второй проход по девяти парам. (12,7) — оба меньше, мимо. (7,9) — мимо. (9,24) — девятка меньше среднего, мимо: условие требует обоих.
- 5. Шаг 4. (24,15) — 24 > 14,4 ✔ и 15 > 14,4 ✔ Пара подходит. kolichestvo = 1, сумма 39 > 0 → maksimum = 39.
- 6. Шаг 5. (15,3) — тройка меньше, мимо. (3,8) — мимо. (8,21) — восьмёрка меньше, мимо. (21,5) — пятёрка меньше, мимо. (5,40) — пятёрка меньше, мимо.
- 7. Шаг 6. Ответ: 1 39.
- 8. Шаг 7. Что было бы при округлении. Если бы среднее посчитали целочисленно,
144 // 10дало бы 14, и элемент 15 всё равно прошёл бы. Но возьмите последовательность со средним 15,7 и элементом 15: при честном делении он не проходит, при округлённом до 15 — сравнение15 > 15тоже ложно, а вот при округлении в большую сторону до 16 ответ изменится. Не округляйте вовсе — тогда и думать не о чем.
Ответ: 1 39
Разбор примера
Ещё одна подстановка: модуль разности делится на 13
Та же последовательность 12, 7, 9, 24, 15, 3, 8, 21, 5, 40. Определите количество пар, модуль разности элементов которых делится на 13 без остатка, и максимальную из сумм таких пар.
Показать решение по шагам
- 1. Меняется ровно одна строка шаблона:
if abs(x - y) % 13 == 0:. Всё остальное — чтение файла, цикл по индексам, два накопителя, печать — остаётся нетронутым. - 2. Выпишем модули разностей по всем девяти парам: |12−7| = 5, |7−9| = 2, |9−24| = 15, |24−15| = 9, |15−3| = 12, |3−8| = 5, |8−21| = 13, |21−5| = 16, |5−40| = 35.
- 3. Делится на 13 ровно одно значение — 13 у пары (8, 21). kolichestvo = 1, сумма 29 → maksimum = 29.
- 4. Ответ: 1 29.
- 5. Почему именно
abs. Разностьx - yбывает отрицательной, а в Python остаток от деления отрицательного числа неотрицателен:-13 % 13даёт 0, и здесь сработало бы. Но-14 % 13даёт 12, а не −1, и на условиях вида «разность делится на k» это не подводит, а вот на условиях вида «разность меньше k» — подводит сразу. Привычка брать модуль там, где в условии написано «модуль разности», избавляет от необходимости каждый раз это проверять. - 6. Проверка нулевой разности: если бы в последовательности оказались два одинаковых соседних числа, модуль разности был бы 0, а ноль делится на 13 без остатка. Такая пара считается — условие не требует, чтобы разность была ненулевой.
Ответ: 1 29
Если файл устроен не так, как ожидалось
Шаблон опирается на то, что в файле по одному числу в строке. Так и бывает в линии 17, но проверить стоит — на это уходит пять секунд, а ошибка иначе будет искаться долго.
Откройте файл и посмотрите на первые строки. Возможны три ситуации.
По одному числу в строке — обычный случай. a = [int(x) for x in open('17.txt')] работает как есть.
Несколько чисел в строке через пробел. Тогда список строится иначе:
a = [int(x) for x in open('17.txt').read().split()]
split() без аргументов режет по любому количеству пробелов и переводов строки сразу, поэтому один этот вариант покрывает и предыдущий случай. Если сомневаетесь — пишите сразу его.
Первая строка — количество чисел. Такое встречается в линиях 26 и 27, и если её не отделить, счётчик попадёт в данные и испортит и сумму, и среднее:
f = open('17.txt')
n = int(f.readline())
a = [int(x) for x in f]
Проверить себя проще всего печатью: print(len(a), a[:5], a[-5:]). Длина должна совпадать с объявленной в условии или с числом из первой строки, а первые и последние элементы — выглядеть правдоподобно. Одна эта строчка ловит почти все ошибки чтения, и убирают её только перед записью ответа.
Вопрос на проверку
В последовательности 10 элементов. Сколько в ней пар идущих подряд элементов?
Ответить и проверить себя — после бесплатной регистрации.
Вопрос на проверку
Как записать условие «ровно один элемент пары чётный»?
Ответить и проверить себя — после бесплатной регистрации.
Ловушки линии 17
Непересекающиеся пары. Каждый элемент, кроме первого и последнего, входит в две пары — как правый сосед и как левый. Разбиение последовательности на пары «через одну» уменьшает ответ примерно вдвое.
Выход за границу. for i in range(len(a)) вместо len(a) - 1 даёт IndexError на последнем шаге. Ошибка заметная — программа падает, — и потому не опасная; опаснее обратная, когда пишут len(a) - 2 и теряют последнюю пару.
Включающее «или» вместо строгого выбора одного из двух. Разобрано выше: включающее «или» захватывает лишние пары.
Среднее в одном проходе. Величина, вычисляемая по всей последовательности, должна быть посчитана до начала отбора.
Максимум вместо суммы. Условие просит «максимальную из сумм элементов таких пар», а не максимальный элемент среди подходящих пар. Сравнивать надо x + y, а не x и y по отдельности.
Порядок двух чисел в ответе. Сначала количество, потом максимальная сумма. Переставленные местами числа — это ноль баллов при полностью верной программе, и случается это чаще, чем хотелось бы.
Отладка на маленьком файле. В условии всегда есть типовой пример с известным ответом. Прогоните программу на нём, сверьте — и только потом запускайте на боевом файле.
Что унести из урока
Список в Python — это последовательность с доступом по индексу с нуля; a[-1] — последний элемент, срез a[i:i+2] — пара соседних. Готовые функции len, sum, max, min, a.count(x) закрывают почти всю арифметику линии 17.
Вся линия 17 — один шаблон из восьми строк: прочитать файл списком, пройти по индексам до len(a) - 1, взять пару a[i], a[i+1], проверить условие, обновить два накопителя, напечатать их через пробел. Меняется в нём одна строка условия, и таблица выше даёт её для всех формулировок банка.
Два места требуют отдельного внимания. «Ровно один» — это исключающее «или», короче всего записываемое как нечётность суммы. «Больше среднего арифметического всех элементов» — это два прохода, причём среднее считается обычным делением, без округления.
И помните про форму ответа: два числа через пробел, сначала количество, затем максимальная из сумм.
Задание №17 в формате экзамена
Ответить и проверить себя — после бесплатной регистрации.
Задание №17 в формате экзамена
Ответить и проверить себя — после бесплатной регистрации.