Теория
Порядок чисел
Представь размеры файлов: 8, 3, 6, 3 и 5 Кбайт. Если нужно найти два самых маленьких, удобнее расположить числа по возрастанию: 3, 3, 5, 6, 8. Такое упорядочивание называют сортировкой. Повторяющаяся тройка не исчезает: это два файла одинакового размера.
В Python sorted(values) создаёт новый упорядоченный список и не меняет исходный. По умолчанию числа идут от меньшего к большему. Добавка reverse=True меняет направление на убывание. Здесь True означает «да, перевернуть порядок». Списки и их позиции подробно разобраны в уроке «Списки», а различие способов сортировки — в уроке «Сортировка и поиск».
sizes = [8, 3, 6, 3, 5]print(sorted(sizes)) # [3, 3, 5, 6, 8]print(sorted(sizes, reverse=True)) # [8, 6, 5, 3, 3]print(sizes) # [8, 3, 6, 3, 5]- По убыванию размеры идут как 8, 6, 5, 3, 3.
- Позиции списка начинаются с нуля: второе значение имеет индекс 1.
- Ответ — 6. Если взять вторую позицию списка по возрастанию, получится 3: направление действительно меняет ответ.
Данные из файла
На экзамене чисел бывает слишком много, чтобы переписывать их в программу. Часто первая строка файла сообщает количество следующих записей, а сами значения начинаются со второй. Такую первую строку называют заголовком. Если принять её за очередной размер, ответ изменится.
Пусть файл sizes.txt содержит четыре строки: сначала 3, затем 7, 2 и 5. Тройка здесь означает количество размеров, а не размер файла. int(line) превращает текст строки в число, append добавляет его в список. Чтение файла подробнее разобрано в уроке «Файлы».
with open("sizes.txt", encoding="utf-8") as source: count = int(source.readline()) sizes = [] for line in source: sizes.append(int(line)) print(count) # 3print(sorted(sizes)) # [2, 5, 7]Чтобы выполнить код, создай рядом с ним файл с указанными четырьмя строками. readline() читает ровно первую; последующий цикл получает остальные. Сравни count с len(sizes): если они различаются, файл прочитан неверно или неполон. Если строка содержит несколько значений, метод split() разделяет её по пробелам; каждую часть затем превращают в число отдельно.
В другом файле одна строка может описывать сразу несколько свойств объекта. Например, [2, 92] означает номер записи 2 и её 92 очка. Числа должны перемещаться вместе: если отсортировать только очки, их связь с номерами потеряется. Для сортировки таких записей нужно указать поле сравнения — здесь очки.
Параметр key получает правило, которое извлекает из каждой записи сравниваемое значение. Запись lambda record: record[1] означает: «получи запись и верни её второй элемент». lambda — короткая запись функции без имени; здесь её единственная работа — выбрать очки. Добавка reverse=True ставит большие очки первыми. При равных очках sorted сохраняет прежний взаимный порядок записей. Это свойство называют устойчивой сортировкой. В опубликованном уроке о сортировке записей подробнее показано, как выбрать поле.
records = [[1, 75], [2, 92], [3, 92], [4, 84], [5, 75], [6, 92]]ordered = sorted(records, key=lambda r: r[1], reverse=True)print(ordered)# [[2, 92], [3, 92], [6, 92], [4, 84], [1, 75], [5, 75]]print(ordered[2][0]) # 6В ordered[2][0] сначала выбирается третья запись, затем её первый элемент — номер. Три записи с 92 очками остались в прежнем порядке: 2, 3, 6. Если вместо сортировки по одному полю развернуть весь результат, порядок равных записей поменяется и правило задачи нарушится.
- Первая строка 4 сообщает количество записей; её не добавляем.
- В список попадут 10, 1, 6 и 1 — четыре элемента.
- После сортировки получится [1, 1, 6, 10]. Повторяющиеся единицы остаются.
Отбор при ограничении
На носителе осталось 13 Кбайт, а файлы занимают 8, 6, 5, 4, 3 и 2 Кбайт. Нужно поместить как можно больше файлов. Если сначала взять размер 8, места для других останется мало. Если начать с самых маленьких, поместятся 2, 3 и 4: вместе 9.
Почему три — максимум? Любые четыре файла занимают не меньше, чем четыре самых маленьких: 2 + 3 + 4 + 5 = 14. Четвёрка уже не помещается. Выбор самого маленького доступного значения на каждом шаге называют жадным выбором. Он доказан здесь для цели увеличить количество при одинаковой важности каждого файла; в другой задаче правило придётся обосновать заново.
sizes = sorted([8, 6, 5, 4, 3, 2])capacity = 13used = 0chosen = []for size in sizes: if used + size <= capacity: chosen.append(size) used += size print(chosen) # [2, 3, 4]print(len(chosen)) # 3Знак <= допускает точное заполнение. Например, при вместимости 9 файлы 2, 3 и 4 занимают место ровно целиком и должны учитываться. Строгое < отбросило бы эту тройку. После первого неподходящего размера оставшиеся, ещё большие размеры тоже не поместятся; просмотр продолжается только ради простоты кода.
- По возрастанию: 2, 3, 5, 6.
- 2 + 3 + 5 = 10: три файла помещаются ровно.
- Все четыре занимают 16, значит максимальное количество — 3.
Второе условие
Часто спрашивают два числа: сначала наибольшее количество файлов, затем наибольший размер одного файла среди наборов с таким количеством. Это разные вопросы. Выбор 2, 3, 4 даёт три файла, но самый крупный из них имеет размер 4. Можно заменить 4 на 8: сумма 2 + 3 + 8 = 13, количество остаётся три, а второй ответ становится 8.
Сначала находим максимальное количество count. Чтобы оставить место одному крупному файлу, остальные count - 1 берём самыми маленькими. Любой другой набор этих остальных файлов занимает не меньше места. Затем проверяем кандидатов на место последнего. В примерах ниже хотя бы один файл всегда помещается; если в иной задаче это не гарантировано, случай нулевого количества нужно разобрать отдельно.
В следующем коде sizes[:count - 1] берёт начало списка до позиции count - 1, не включая её. Такой фрагмент списка называют срезом. Здесь получатся размеры 2 и 3. Функция sum(...) складывает все числа среза: sum([2, 3]) равно 5.
sizes = sorted([8, 6, 5, 4, 3, 2])capacity = 13used = 0count = 0for size in sizes: if used + size <= capacity: used += size count += 1 small_total = sum(sizes[:count - 1])best_last = 0for index in range(count - 1, len(sizes)): if small_total + sizes[index] <= capacity: best_last = sizes[index] print(count, best_last) # 3 8Срезы подробнее разобраны в уроке о списках. Цикл рассматривает остальные размеры по возрастанию. Каждое новое подходящее значение улучшает best_last. Равные размеры не удаляются: два файла одного размера остаются двумя элементами списка.
- Два остальных файла берём самыми маленькими: 2 и 3 занимают 5.
- Остаётся 8. Файл размера 8 помещается в точности: 2 + 3 + 8 = 13.
- Три файла сохранены, а наибольший размер среди них равен 8.
Журнал событий
Не все задачи 26 устроены как список размеров. В проекте демонстрационного варианта 2027 года ФИПИ предложен журнал запросов сервера: у каждой строки есть время, номер клиента и объём данных. Память ограничена; перед запросом, который уже не помещается, накопленное копируют и очищают. Эту временную память называют буфером: в нём лежат данные запросов до копирования, а готовые копии хранятся отдельно. Здесь важен порядок событий: перестановка строк по объёму меняет размеры копий. Числа ниже придуманы специально для урока.
Запись — несколько значений об одном событии. В коде запись представлена тройкой (время, клиент, объём). Тройки уже идут по времени; при равном времени сохраняется порядок строк файла. Словарь totals хранит сумму объёмов отдельно для каждого клиента: ключом служит его номер. Незнакомый ключ начинаем с нуля. Словари подробнее разобраны в уроке Python.
В строке for time, client, volume in requests три имени слева получают три значения очередной записи по порядку: время попадает в time, номер клиента — в client, объём — в volume. Аналогично запись time, group, points = line.split() раздаёт трём переменным три части строки, разделённой пробелами. Количество частей и переменных должно совпадать.
В наших коротких данных строки уже расположены по времени. Если в другом файле они перемешаны, сначала выясни, в каком порядке их требуется обработать. Для хронологического порядка можно применить sorted(requests, key=lambda record: record[0]): здесь поле с номером 0 — время. При одинаковом времени устойчивый способ сортировки Python сохранит порядок строк файла. Если условие задаёт другое правило для равных времён, следуй именно ему.
Вместимость памяти равна 10. Первый запрос занимает 4, второй — 5: вместе 9. Третий объём 3 уже не помещается. Перед ним создаётся копия размером 9, а 3 начинает новое заполнение. При точном равенстве, например 4 + 6 = 10, копия не нужна. В этом примере временем копии считается время запроса, который не поместился.
requests = [ ("09:00:00", 1, 4), ("09:01:00", 2, 5), ("09:02:00", 1, 3), ("11:59:59", 2, 8), ("12:00:00", 3, 6),]capacity = 10used = 0totals = {}early_copies = []for time, client, volume in requests: if used + volume > capacity: if time <= "11:59:59": early_copies.append(used) used = 0 used += volume if client not in totals: totals[client] = 0 totals[client] += volume print(totals) # {1: 7, 2: 13, 3: 6}print(early_copies) # [9, 3]Время можно сравнивать как текст только потому, что все строки имеют одинаковый формат ЧЧ:ММ:СС с ведущими нулями. Момент 11:59:59 входит в ограничение «не позднее», а 12:00:00 уже нет. Четвёртый запрос вызывает раннюю копию размером 3. Пятый вызывает копию размером 8, но после полудня. Содержимое, оставшееся в памяти, ещё не является сделанной копией.
- Клиент 1 передал 4 + 3 = 7, клиент 2 — 5 + 8 = 13, клиент 3 — 6. Лидирует клиент 2.
- До 11:59:59 завершены копии размеров 9 и 3. Их сумма 12.
- Два числа ответа: 2 и 12. Итоги клиентов учитывают все запросы, а граница времени относится только к копиям.
Проверка ответа
Большой файл невозможно проверить глазами целиком. Создай маленький набор, где ответ виден вручную. Для отбора выпиши все допустимые группы и сравни их количество и самый крупный элемент. Для журнала запиши после каждой строки, сколько занято в памяти, нужна ли копия и как изменилась сумма клиента. Только после этого запускай ту же программу на большом файле.
Сюжеты задания 26 бывают разными. В одном условии ищут свободные места в ряду, в другом — выбирают занятия без наложения по времени, в третьем — считают продажи одинакового товара. Там тоже придётся читать записи и проверять границы, но правило выбора будет другим. Сначала выясни, что означает строка файла и какие записи можно объединять или сравнивать. Лишь затем решай, нужна ли сортировка и по какому полю.
- Прочитай формат. Отдели заголовок от записей и проверь количество прочитанных строк.
- Назови порядок. Укажи направление сортировки или сохрани хронологию событий. При равном времени не меняй порядок без правила условия.
- Проверь границы. Испытай точное заполнение, повторяющиеся размеры и время ровно на границе.
- Пересчитай оба числа. Первый и второй показатели могут требовать разных проходов; сверь каждый отдельно.
Если у двух клиентов одинаковая максимальная сумма, ответ без дополнительного правила неоднозначен. Условие должно указать, какой номер выбрать, либо гарантировать единственного лидера. То же относится к случаю, когда до границы времени сделано меньше двух копий. В задачах урока эти случаи определены заранее.
На экзамене
В задании 26 данные часто приходят из файла, а ответ состоит из двух чисел. Уточни, что означает каждое число и в каком порядке их записывать. Сортировка помогает подготовить данные, но не заменяет правило отбора или обработку событий.
Перед большим файлом проверь программу на коротком примере. Особенно внимательно проверь направление сортировки, равенство вместимости, повторяющиеся значения, момент копирования и границу времени.
Практика
Практика временно недоступна. Можно продолжить читать теорию.
Итог
Что получилось
Теперь ты можешь прочитать числовые данные из файла, выбрать направление сортировки и объяснить, почему оно помогает решить задачу. При ограниченной вместимости ты сначала находишь максимальное количество, а затем отдельно улучшаешь второй показатель. Если данные описывают события, ты сохраняешь их хронологию и обновляешь состояние после каждой строки.
Решай задачи по порядку: от сортировки нескольких чисел до журнала запросов. При ошибке вернись к короткому примеру и проверь границу, повторяющиеся значения и то, какие данные участвуют в каждом из двух ответов. Потом повтори задачу без подсказки.