Это не какой-то алгоритм, а скорее простая идея о том, как решаются многие задачи.
Есть купюры и монеты номиналами:
Выпишем первые несколько ответов на задачу:
ans[1] = 1; # 1
ans[2] = 2; # 1 1
ans[3] = 3; # 1 1 1
ans[4] = 4; # 1 1 1 1
ans[5] = 1; # 5
ans[6] = 2; # 5 1
ans[7] = 3; # 5 1 1
ans[8] = 4; # 5 1 1 1
ans[9] = 5; # 5 1 1 1 1
ans[10] = 1; # 10
Хочется сказать, что оптимальным алгоритмом будет следующее: взять максимум купюр номинала
В общем смысле жадный алгоритм - это брать элементы в порядке уменьшения чего-нибудь, брать самый большой элемент первым. Эта одна простая идея объединяет множество разных задач.
Чем-то это похоже на математический "принцип крайнего" - всегда будет полезно посмотреть на самый крайний (максимальный или минимальный элемент), очень часто решение начинается именно с него.
Плохо просто предполагать, что в задаче жадный алгоритм работает, потому что часто бывает, что хоть жадность и кажется очевидным решением в задаче, то это оказывается неверным. Поэтому прежде, чем писать жадность, стоит её доказать.
Вместо того, чтобы доказывать утверждение про жадность для конкретной задачи про числа
Если каждый следующий номинал делится на предыдущий, то жадный алгоритм работает.
Пусть купюры имели номинал
Пусть в оптимальном ответе купюра с номиналом
Давайте посчитаем, какого достоинства может быть сумма всех достоинств всех купюр в оптимальном ответе, если не считать максимальные купюры. Это будет
Из этого делаем вывод, что если
Теперь, когда жадность доказана, можно предъявить алгоритм:
vector<int> n = {1, 2, 5, 10, 50, 100, 1000, 2000, 5000}
int sums, ans = 0;
cin >> sums;
for (int i = 8; i >= 0; i--) {
ans += sums / n[i];
sums %= n[i];
}
cout << sums << endl;
Пусть есть рюкзак с вместимостью не более, чем
Также будем решать эту задачу жадно. Отсортируем предметы по убыванию "плотности ценности"
Давайте представим, что мы уже поделили все предметы на кусочки веса 1 грамм, при этом их ценность стала равна
Заметим, что в жадном алгоритме мы как раз и набираем максимальные по
Предьявим алгоритм:
pair<int, int> items[n];
int c, w, W; // стоимость и вес предмета
cin >> W;
for (int i = 0; i < n; ++i) {
cin >> c >> w;
items[i] = {c, w};
}
sort(items, items + n);
int ans = 0;
for (int i = n - 1; i >= 0; --i) {
ans += min(items[i].second, W) * items[i].first;
W -= min(items[i].second, W);
}
cout << ans << endl;
Итоговая асимптотика:
Заметим, что если предметы резать нельзя, такой алгоритм не сработает. Как решать задачу для такого случая обсудим на одной из следующих лекций.
Условие
Даны заявки на проведение занятий в некоторой аудитории. В каждой заявке указаны начало и конец занятия
Решение
Здесь жадность становится не такой уже очевидной, потому что неясно в каком порядке рассматривать заявки, те непонятно как "жадно" их набирать.
Давайте посмотрим на самую первую по времени конца заявку. Заметьте, что нам всегда выгодно включить её в оптимальный ответ - она заканчивается раньше всех остальных, а поэтому если в оптимальном ответе самая первая заявка - другая, мы можем безболезненно заменить её на самую первую по времени конца, и новых пересечений не появится, так как мы просто сдвинули самую первую заявку еще левее.
Раз всегда есть оптимальный ответ, в котором выбрана эта самая левая по времени конца заявка, давайте её возьмем, и выберем самую первую по времени конца заявку из оставшихся, не пересекающихся с той.
Из такого рассуждения про одну самую левую по времени конца заявку следует сразу и общее жадное решение задачи - нужно идти слева направо по заявкам, которые отсортированы по времени конца, и брать новую, если можем, то есть если её начало не раньше, чем конец самой последней уже выбранной.
Вам нужно решить как можно больше задач из этого контеста:
https://informatics.msk.ru/mod/statements/view3.php?id=35449