К содержанию

Подтема 3 из 7

Цепочки коробок и пары

Вложенные коробки снаружи внутрь и изнутри наружу, рейсы по двое, грузы и контейнеры, детали двух видов.

Цепочка снаружи

Коробки со сторонами 12, 18, 21, 27, 34, 40 и 45; одну можно вложить в другую, если её сторона меньше хотя бы на 6. Как вложить друг в друга как можно больше коробок?

Строить цепочку снаружи: в каждую коробку класть самую большую из тех, что в неё помещаются. Большая коробка внутри оставляет больше места для следующих.

Начнём снаружи. Самая большая коробка — 45. В неё можно поместить коробку размером не больше 45 − 6 = 39. Из подходящих выбираем самую большую — 34. Почему не 12? Потому что после выбора 12 внутри почти не останется возможностей; коробка 34 оставляет больше места для последующих вложений.

  1. Размеры по убыванию: 45, 40, 34, 27, 21, 18, 12. Начинаем снаружи — с самой большой коробки.
  2. Внешняя коробка — самая большая, 45.
  3. В 45 помещается размер не больше 45 − 6 = 39. 40 не подходит: разница с 45 меньше 6. Берём наибольший подходящий — 34.
  4. В 34 помещается размер не больше 34 − 6 = 28. Берём наибольший подходящий — 27.
  5. В 27 помещается размер не больше 27 − 6 = 21. Берём наибольший подходящий — 21.
  6. В 21 помещается размер не больше 21 − 6 = 15. 18 не подходит: разница с 21 меньше 6. Берём наибольший подходящий — 12.
  7. Больше вложить нечего: 5 коробок, самая маленькая — 12.

Получилась цепочка 45 → 34 → 27 → 21 → 12 — пять коробок. Код: размеры по убыванию, коробка берётся, если она меньше последней выбранной хотя бы на зазор. Самая маленькая коробка — 12: это ответ на частый второй вопрос «наибольший размер самой маленькой коробки».

sizes = [12, 18, 21, 27, 34, 40, 45]gap = 6sizes.sort(reverse=True)selected = []for size in sizes:    if not selected or selected[-1] - size >= gap:        selected.append(size)print(len(selected), selected[-1])
5 12

Здесь предполагается, что исходный список непустой и gap > 0. Условие истинно, пока цепочка пуста: первая, самая большая коробка берётся без проверки. Благодаря короткому вычислению or к selected[-1] пустого списка программа не обращается.

Обоснование · необязательноПочему жадная цепочка самая длинная

На каждой глубине жадная цепочка держит самую большую возможную коробку, поэтому всё, что помещается в чужую цепочку, поместится и в неё.

Пусть жадная цепочка g₁ > g₂ > …, а другая допустимая цепочка c₁ > c₂ > … > cₘ. Покажем по шагам, что gᵢ ≥ cᵢ для каждого i ≤ m. Для i = 1 это верно: g₁ — самая большая коробка вообще. Пусть gᵢ ≥ cᵢ. Коробка cᵢ₊₁ ≤ cᵢ − 6 ≤ gᵢ − 6, значит, она подходит для вложения в gᵢ, и жадный алгоритм найдёт подходящую коробку; он берёт наибольшую из подходящих, поэтому gᵢ₊₁ ≥ cᵢ₊₁. Следовательно, жадная цепочка не короче любой другой, а её i-я коробка не меньше i-й коробки любой другой цепочки — в том числе последняя.

Изнутри наружу

А если второй вопрос — «наименьший размер внешней коробки»?

Строить цепочку изнутри наружу: начать с самой маленькой и каждый раз брать самую маленькую коробку, в которую влезает предыдущая. Строй с той стороны, которую оптимизирует второй критерий.

Рассуждение то же, только зеркальное: маленькая внешняя коробка на каждом шаге оставляет больше выбора для следующей.

sizes = [12, 18, 21, 27, 34, 40, 45]gap = 6chain = []for size in sorted(sizes):    if not chain or size - chain[-1] >= gap:        chain.append(size)print(*chain)
12 18 27 34 40

Пять коробок, внешняя имеет размер 40, а снаружи внутрь внешней была 45. Количество одинаковое, а второе число зависит от направления: строй от той стороны, которую оптимизирует второй критерий.

Этот разбор предполагает одинаковый минимальный зазор между соседними коробками и отсутствие других ограничений. Материалы и цвета, которые должны чередоваться, или зазор, растущий с номером коробки, — это усложнения из дополнительного урока: для них нужно новое обоснование.

Два указателя

В лодку садятся не больше двух человек, суммарная масса не больше вместимости. Как перевезти всех за наименьшее число рейсов?

Отсортировать массы и каждый раз сажать самого тяжёлого из оставшихся — вместе с самым лёгким, если они помещаются вдвоём. Два конца списка отмечают два указателя.

Указатель здесь — обычная переменная, хранящая индекс текущего элемента: left = 0 стоит в начале списка, right = len(a) - 1 — в конце.

Предполагается, что любой человек может ехать один. Если самый тяжёлый не помещается даже с самым лёгким, ни с кем другим он тоже не поместится — значит, едет один.

  1. Массы по возрастанию. left — самый лёгкий из оставшихся, right — самый тяжёлый.
  2. 50 + 130 = 180 > 150: с самым лёгким не помещается — значит, ни с кем. 130 едет один.
  3. 50 + 100 = 150 ≤ 150: самый тяжёлый едет с самым лёгким, оба указателя сдвигаются.
  4. 60 + 80 = 140 ≤ 150: самый тяжёлый едет с самым лёгким, оба указателя сдвигаются.
  5. Остался один человек, 70: он едет один.
  6. Всё: 4 поездки.
w = [80, 130, 50, 100, 70, 60]capacity = 150w.sort()left = 0right = len(w) - 1trips = 0while left <= right:    if left < right and w[left] + w[right] <= capacity:        left += 1    right -= 1    trips += 1print(trips)
4

При совместной поездке двигаются оба индекса: использованы два человека. Если вместе не помещаются, используется только тяжёлый — двигается правый индекс. При left == right остался один человек, и условие left < right не позволяет использовать его дважды. Каждый указатель проходит список один раз, поэтому после сортировки работа — O(N).

Обоснование · необязательноПочему самого тяжёлого можно посадить с самым лёгким

Обоснование строится обменом: самого тяжёлого можно объединить с самым лёгким, не ухудшая необходимое количество пар. Если в другом размещении они ехали с другими людьми, пары можно перестроить: тяжёлый поедет с лёгким, а их бывшие партнёры — вместе. Оставшиеся партнёры совместимы, поскольку каждый из них не тяжелее самого тяжёлого, а тот помещался с лёгким. Число рейсов от перестройки не растёт.

Ограничение «не более двух» существенно. Для произвольного числа объектов в рейсе этот алгоритм уже не является решением общей задачи упаковки.

Грузы и контейнеры

Грузы и контейнеры даны двумя списками, в контейнер помещается один груз. Как отправить как можно больше грузов?

Идти по грузам от лёгких и каждому отдавать самый маленький из подходящих контейнеров. Большие контейнеры остаются тяжёлым грузам.

Иногда в строке файла два числа, которые не связаны между собой: масса груза и вместимость контейнера, причём каждый контейнер берёт один груз любой массы, не большей его вместимости. Это то самое исключение из правила «сортировать записи целиком»: грузы и контейнеры — два отдельных списка, и сортируют их по отдельности.

Пусть массы грузов 15, 40, 25, 48, 30, а вместимости контейнеров 20, 10, 50, 35, 45. Лёгкие грузы получают самые маленькие подходящие контейнеры: 15 → 20, 25 → 35, 30 → 45, 40 → 50, а для 48 контейнера уже нет. Отправлено 4 груза.

Почему так правильно? Самому лёгкому грузу подходит любой контейнер, подходящий другим; отдавая ему самый маленький, мы сохраняем большие контейнеры для тяжёлых грузов — снова «оставить больше возможностей».

Второй вопрос — наибольшая масса отправленного груза при максимальном количестве — решается приёмом из подтемы 2: закрепляем k − 1 = 3 самых лёгких груза в их самых маленьких контейнерах (20, 35, 45), а последний груз берём наибольший из тех, что помещаются в самый большой из оставшихся контейнеров — 50. Это 48, а не 40.

goods = sorted([15, 40, 25, 48, 30])boxes = sorted([20, 10, 50, 35, 45])count = j = 0for g in goods:    while j < len(boxes) and boxes[j] < g:        j += 1    if j == len(boxes):        break    count += 1    j += 1 used = [False] * len(boxes)j = 0for g in goods[:count - 1]:    while boxes[j] < g:        j += 1    used[j] = True    j += 1room = max(b for b, u in zip(boxes, used) if not u)print(count, max(g for g in goods[count - 1:] if g <= room))
4 48

Первый цикл считает количество, второй закрепляет k − 1 самых лёгких грузов: отмечает, какие контейнеры они заняли. Указатель j по контейнерам только растёт: контейнер, который мал для лёгкого груза, тем более мал для следующих, более тяжёлых.

Детали двух видов

Изделие собирают из детали A и детали B, размеры которых отличаются не больше чем на D. Как собрать больше изделий с наибольшей прибылью?

Отсортировать оба списка по убыванию и соединять крупнейшие совместимые детали; деталь, которой нет пары, пропускать.

Прибыль — сумма размеров, все размеры положительные. Сортируем оба списка по убыванию. Если самая большая A больше самой большой B более чем на D, эта A не подойдёт ни к одной оставшейся B — её можно пропустить. Аналогично для слишком большой B. Если крупнейшие совместимы — объединяем их.

Пусть A = 50, 42, 30, 12, B = 47, 33, 20, 18, D = 5. Пара 50 и 47 подходит; 42 больше 33 + 5 — деталь 42 пропускаем; 30 и 33 подходят; 20 больше 12 + 5 — пропускаем 20, затем и 18. Получаем 2 изделия с прибылью 97 + 63 = 160.

a = sorted([50, 42, 30, 12], reverse=True)b = sorted([47, 33, 20, 18], reverse=True)distance = 5i = j = 0count = profit = 0while i < len(a) and j < len(b):    if a[i] > b[j] + distance:        i += 1    elif b[j] > a[i] + distance:        j += 1    else:        count += 1        profit += a[i] + b[j]        i += 1        j += 1print(count, profit)
2 160
Обоснование · необязательноПочему можно объединить крупнейшие совместимые детали

Если в другом решении крупнейшие совместимые детали соединены с другими партнёрами, эти две пары можно перестроить: крупнейшие — вместе, их бывшие партнёры — вместе. Меньшие оставшиеся партнёры сохранят совместимость: их разность ограничивается одной из уже допустимых разностей. Если одна из крупнейших деталей не использовалась, замена меньшей детали на неё не уменьшает прибыль.

Здесь доказательство использует конкретную совместимость по разности размеров и конкретную формулу прибыли. При других правилах оно может перестать работать. Проверка перебором на тысячах маленьких наборов подтверждает: этот проход даёт и наибольшую прибыль, и наибольшее количество изделий.

Итого

  • Снаружи внутрьв каждую — самую большую подходящую
  • Изнутри наружуесли второй критерий — внешняя коробка
  • Рейсы по двоетяжёлый с лёгким, два указателя
  • Грузы и контейнерылёгкий груз — в самый маленький подходящий
  • Детали A и Bкрупнейшие совместимые, лишнюю пропустить
  • Обоснованиевыбор оставляет больше возможностей

Проверьте себя

Ответь сначала сам, потом открой ответ.

Вопрос 1
Размеры 5, 9, 9, 13, 20, 22, разность не меньше 4. Найди длину цепочки снаружи внутрь и самую маленькую коробку. Могут ли обе коробки 9 оказаться в одной цепочке?
ОтветСкрыть ответ
Ответ
4 5: 22, 13, 9, 5. Нет: разность 9 − 9 = 0 меньше 4.
Вопрос 2
Коробки 10, 13, 16, 19, 25, зазор 3. Какая цепочка получится снаружи внутрь?
ОтветСкрыть ответ
Ответ
25, 19, 16, 13, 10 — все пять коробок: каждая следующая меньше хотя бы на 3.
Вопрос 3
Массы 40, 40, 50, вместимость 130. Сколько рейсов даст алгоритм двух указателей? А если в рейсе может быть сколько угодно человек?
ОтветСкрыть ответ
Ответ
2 рейса: 50 + 40 = 90 вместе, второй 40 — один. Без ограничения хватит одного рейса: 40 + 40 + 50 = 130.
Вопрос 4
Грузы 10, 20, 30, контейнеры 15, 25, 25. Сколько грузов отправим и какой наибольший груз может быть среди них?
ОтветСкрыть ответ
Ответ
2 и 20: 10 → 15, 20 → 25, а 30 не помещается ни в один контейнер.
Вопрос 5
Детали A = 20, 14 и B = 18, 9, разность не больше 3. Сколько изделий получится и какая прибыль?
ОтветСкрыть ответ
Ответ
Одно, прибыль 38: 20 и 18 подходят, а 14 больше 9 + 3 — пары у неё нет.

Практика

Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.

Матрёшка с разницей ровно 1

Кубические коробки со сторонами 3, 5, 6, 7, 7, 9, 10, 11, 12, 15. Коробку можно вложить в другую, только если её сторона ровно на 1 меньше. Найди наибольшее число коробок в одной матрёшке и наибольшую сторону самой маленькой коробки в такой матрёшке.

Введите два числа через пробел в том порядке, в каком их спрашивают.

Цепочка с зазором 6

Размеры коробок 10, 16, 21, 27, 34, 40, минимальная разность соседних коробок — 6. Найди максимальную длину цепочки и максимальный размер самой маленькой коробки при такой длине.

Введите два числа через пробел в том порядке, в каком их спрашивают.

Матрёшка с зазором 7

В файле 26-6.txt в первой строке записано количество коробок N, в каждой следующей — длина стороны коробки (натуральное число, не больше 10 000). Одну коробку можно вложить в другую, если длина её стороны хотя бы на 7 меньше. Определи наибольшее количество коробок в одной «матрёшке» и максимально возможную длину стороны самой маленькой коробки в такой «матрёшке».

26-6.txtСтороны коробокТекстовый файл · 47,8 КБ

Введите два числа через пробел в том порядке, в каком их спрашивают.

Изнутри наружу

Те же коробки. Найди максимальную длину цепочки и минимальный размер самой большой коробки при такой длине.

Введите два числа через пробел в том порядке, в каком их спрашивают.

Рейсы по двое

Массы людей 40, 55, 70, 85, 90, в рейсе не больше двух человек, вместимость 130. Сколько рейсов нужно?

Введите число.

Грузы в контейнеры

Массы грузов 12, 18, 25, 27, вместимости контейнеров 15, 20, 28, 10; в контейнер помещается один груз. Сколько грузов можно отправить и какова наибольшая масса отправленного груза при таком количестве?

Введите два числа через пробел в том порядке, в каком их спрашивают.

Грузы и контейнеры из файла

В файле 26-7.txt в первой строке записано число N, в каждой следующей — два натуральных числа: масса очередного груза и вместимость очередного контейнера. Строка не связывает груз с контейнером: всего N грузов и N контейнеров. В контейнер помещается один груз, масса которого не больше вместимости контейнера. Определи максимальное количество грузов, которое можно отправить, и максимальную массу отправленного груза при условии, что отправлено максимальное количество грузов.

26-7.txtПары чисел: груз и контейнер, заготовки A и BТекстовый файл · 58,2 КБ

Введите два числа через пробел в том порядке, в каком их спрашивают.

Детали A: 31, 44, 52, 60; детали B: 35, 47, 49, 70; разность размеров в изделии не больше 4. Сколько изделий получится и какова прибыль, если она равна сумме размеров деталей?

Введите два числа через пробел в том порядке, в каком их спрашивают.

Заготовки A и B

В том же файле 26-7.txt первое число строки — размер заготовки A, второе — размер заготовки B. Изделие делают из одной заготовки A и одной B, размеры которых различаются не больше чем на 15; прибыль от изделия равна сумме размеров заготовок. Заготовки используют так, чтобы получить наибольшую прибыль. Запиши количество изделий, затем прибыль.

26-7.txtПары чисел: груз и контейнер, заготовки A и BТекстовый файл · 58,2 КБ

Введите два числа через пробел в том порядке, в каком их спрашивают.