Сортировка
Раздать печенье
Раздать детям печенье так, чтобы все они были максимально довольны
Постановка задачи
Описание (LeetCode - 455. Assign Cookies)
Вы замечательный родитель и хотите угостить своих детей печеньем, но каждому ребенку можно дать не более одного печенья.
У каждого ребенка i есть коэффициент жадности g[i] - это минимальный размер
печенья, который его удовлетворит. У каждого печенья j есть размер s[j].
Если s[j] >= g[i], мы можем дать печенье j ребенку i, и он будет
доволен. Наша задача - максимизировать количество довольных детей.
Нужно вернуть максимальное количество довольных детей.
Пример 1:
g = [1, 2, 3]
s = [1, 1]
Результат:
1
Пояснение:
Мы можем отдать печенье размера 1 ребенку с жадностью 1. Оставшееся печенье
размера 1 не подойдет детям с жадностью 2 или 3.
Пример 2:
g = [1, 2]
s = [1, 2, 3]
Результат:
2
Пояснение:
Коэффициенты жадности детей равны 1 и 2. У нас есть 3 печенья, и их
размеры достаточно велики, чтобы удовлетворить обоих детей.
Ограничения:
1 <= g.length <= 3 * 10⁴0 <= s.length <= 3 * 10⁴1 <= g[i], s[j] <= 2³¹ - 1
Решение
Нам нужно раздать печенье так, чтобы удовлетворить как можно больше детей, но каждое печенье можно использовать только один раз. Эта задача отлично решается с помощью сортировки. Если отсортировать детей по жадности, а печенье - по размеру, мы сможем быстро определять, подходит ли текущее печенье текущему ребенку.
Идея решения:
Сначала мы удовлетворяем детей с самой маленькой жадностью. Если небольшое печенье подходит ребенку с малой жадностью, выгодно отдать его. Если оно не подходит, то не подойдет и более жадным детям, поэтому его можно пропустить.
Отсортируем оба массива по возрастанию. Затем будем идти двумя указателями:
iуказывает на текущего ребенка.jуказывает на текущее печенье.
Если s[j] >= g[i], текущее печенье подходит ребенку. Мы увеличиваем ответ и
переходим к следующему ребенку и следующему печенью.
Если s[j] < g[i], печенье слишком маленькое. Оно не подойдет этому ребенку и
всем следующим, так как они еще более жадные. Мы переходим к следующему печенью.
Почему это корректно? Ребенка с самой маленькой жадностью выгодно удовлетворять самым маленьким подходящим печеньем. Так мы не тратим большие печенья там, где хватило бы меньшего, и оставляем больше шансов для остальных детей.
Основные шаги:
- Сортируем
gпо возрастанию. - Сортируем
sпо возрастанию. - Идем по обоим массивам двумя указателями.
- Если печенье подходит ребенку, увеличиваем ответ и двигаем оба указателя.
- Если печенье слишком маленькое, двигаем только указатель печенья.
- Возвращаем количество довольных детей.
Рассмотрим пример:
g = [1, 2, 3]
s = [1, 1]
Массивы уже отсортированы.
- Ребенок
1, печенье1: подходит, ответ стал1. - Ребенок
2, печенье1: не подходит, печенье пропускаем. - Печенье закончилось.
Итоговый ответ равен 1.
Сортировка здесь нужна не ради самого порядка, а ради правильного жадного выбора. После сортировки мы всегда знаем, что текущий ребенок - самый нетребовательный из оставшихся, а текущее печенье - самое маленькое из неиспользованных.
Оценка сложности
Временная сложность
Сортировка массива g длины n занимает O(n log n). Сортировка массива s
длины m занимает O(m log m). После этого мы выполняем один линейный проход.
Итоговая временная сложность: O(n log n + m log m).
Пространственная сложность
Если не учитывать память, необходимую для встроенной сортировки, мы храним
только несколько переменных. Пространственная сложность составляет O(1).
Код решения
Приведем код решения.
func findContentChildren(g []int, s []int) int {
sort.Ints(g)
sort.Ints(s)
i := 0
j := 0
count := 0
for i < len(g) && j < len(s) {
if s[j] >= g[i] {
count++
i++
}
j++
}
return count
}Итоги
Задача Assign Cookies - хороший пример того, как сортировка превращает задачу распределения ресурсов в простой жадный алгоритм.
- Суть алгоритма: мы сортируем детей по жадности, печенье по размеру и всегда пытаемся удовлетворить самого нетребовательного оставшегося ребенка самым маленьким доступным печеньем.
- Эффективность:
- Временная сложность:
O(n log n + m log m), потому что нужно отсортировать оба массива. - Пространственная сложность:
O(1), если не учитывать память встроенной сортировки.
- Временная сложность:
- Граничные случаи:
- Печенья нет: ответ
0. - Все печенья слишком маленькие: указатель печенья дойдет до конца, а
счетчик довольных детей останется равным
0. - Печений больше, чем детей: алгоритм остановится, когда все дети будут удовлетворены.
- Печенья нет: ответ
- Оптимальное решение: сортировка дает порядок, в котором жадный выбор становится корректным. Мы не тратим большое печенье на ребенка, которому хватило бы меньшего, и поэтому максимизируем количество довольных детей.