Недавно я решил задачу по программированию на собеседовании при приеме на работу и получил правильные результаты, но мой код работал недостаточно быстро, и мне не сказали, почему.
Представьте себе жадного продавца, у которого есть куча товаров и определенное количество каждого предмета. Он продает каждый товар по цене, равной его оставшемуся количеству, и хочет заработать как можно больше денег за определенное количество продаж. Я думаю, что количество предметов, количество каждого предмета и количество продаж могут достигать 105? Любое решение на Java должно было выполняться менее чем за 4 секунды.
В приведенном примере было 5 товаров в количествах 10, 10, 8, 9 и 1. За 6 продаж максимальная сумма денег, которую он мог бы заработать, составила бы 10+10+9+9+9+8=55.
Основной способ, который я пробовал, заключался в том, чтобы каждый раз получать максимальное количество и добавлять его к выбранному количеству продаж. Но для тысяч предметов это было слишком медленно. Что-то вроде:
public static long getMaximumAmount(List<Integer> arr, int m) {
long revenue = 0;
int max = 0;
while (m>0) {
max = Collections.max(arr);
revenue += max;
arr.set(arr.indexOf(max),max-1);
m--;
}
return revenue;
}
Затем я каждый раз пробовал сортировать количества и брать таким образом верхний элемент, но это тоже было слишком медленно.
Затем я попробовал отсортировать их один раз, начиная с верхнего элемента и каждый раз сравнивая его с соседними элементами, чтобы таким образом получить максимум, но это тоже было слишком медленно.
И, наконец, я попробовал не сортировать его и начинать с первого элемента и каждый раз сравнивать его с соседними элементами, чтобы получить большее количество, но это не всегда давало правильные результаты, несмотря на то, что оно было быстрее.
Поскольку, за исключением последней попытки, ваша программа работала, похоже, об этом лучше спросить в Code Review . Пожалуйста, прочитайте их Как задать вопрос. Включите в вопрос код для вашей лучшей попытки.
Этот вопрос следует открыть вновь. Речь идет не о Code Review: код был сломан — он неправильно делал то, что требовалось. В данном конкретном случае требование включает в себя производительность. Алгоритмическая сложность полностью соответствует тому, для чего нужен SO.
Более того, ожидание, пока ОП предоставит свой код, хотя это и может успокоить некоторые опасения по поводу того, что «они просто просят нас сделать домашнее задание», почти совершенно бесполезно для ответа на вопрос: как правило, для проблем алгоритмической сложности не существует простого решения. настроить алгоритм, который недостаточно быстр, чтобы сделать его достаточно быстрым. Приходится бросить почти всю работу и начать с нуля.
Это могло бы прояснить ситуацию, но поставленный вопрос достаточно ясен; На вопрос Робби Корнелиссена можно легко ответить, просто внимательно прочитав и добавив немного воображения: Конечно, упражнение предназначено для того, чтобы вы разузнали: «Если владелец магазина продает предмет, это означает, что у владельца магазина теперь на 1 такого предмета меньше». Полностью разочарован голосованием за закрытие здесь.
Немного жаль, что ответ пропадет даром. @user352805 - он здесь pastebin.com/9va3Z804 - ждет повторного открытия.
@rzwitserloot Значит, цена товара снижается по мере уменьшения его оставшегося количества? Это интерпретация?
@RobbyCornelissen Да, именно об этом говорится в вопросе, и это соответствует приведенному примеру. Если бы я мог поговорить с автором этого вопроса, я бы посоветовал им переписать его; «Это магазин» не согласуется с «цена предмета зависит от его количества» - на самом деле, мы интуитивно понимаем цены наоборот (чем более редкий предмет, тем выше должна быть цена). Но поставленный вопрос не является двусмысленным. Это действительно правильная интерпретация.
Я бы проголосовал за повторное открытие, если бы ОП отредактировал вопрос, чтобы показать некоторый код... В противном случае не стоит стрелять в темноте.
@MarsAtomic Это вопрос алгоритмической сложности; ни одной строчки из того, что они написали, в «фиксе» не останется.
@RobbyCornelissen Да, цена всегда равна оставшемуся количеству. Таким образом, цена снижается на один каждый раз, когда он продается. Я добавил слово в свое описание, чтобы, надеюсь, прояснить это.
Я тоже уже проголосовал за открытие. Просто жду еще одного голосования.
Ваш код неполный; в частности, похоже, отсутствует функция main() и хотя бы одна import. Пожалуйста, отредактируйте свой код так, чтобы он представлял собой минимально воспроизводимый пример вашей проблемы (включая все необходимые входные данные, но желательно, чтобы они не требовались), тогда мы сможем попытаться воспроизвести и решить ее. Вам также следует прочитать Как спросить.
@rzwitserloot, за изысканную простоту вашего решения вы заслуживаете медали и спасибо за объяснение.




сортировка списка целых чисел 10^5 — дело миллисекунды. Если ограничение составляет «4 секунды», это фактически бесплатная операция. Чаще всего при решении подобных задач вас действительно беспокоит то, что называется алгоритмической сложностью; обычно обозначается буквой «большое О». Сортировка — это O(n * log n) {n = size of input}, и, как правило, поворотный момент — это O(n^2) — все, что медленнее этого, обычно «слишком медленно», а все, что быстрее, обычно «достаточно быстро», и на кону что-нибудь… путаница. Это все… вещи, для полного освоения которых требуется 5 лет обучения в университете, а Stack Overflow — не лучшее место для публикации книги на 1000 страниц, поэтому обобщений будет достаточно.
Когда у нас есть отсортированный список, этот алгоритм действительно становится очень простым. В конце концов, вы хотите продать тот предмет, которого у вас больше всего, и он находится в начале списка.
Нам необходимо принять во внимание, что продажа этого предмета не приводит к полному исчезновению его запасов; он просто вычитает 1. Самый «быстрый» способ позаботиться об этом — осознать, что если продолжать продавать товар, который начинается с самого высокого запаса, неправильно, алгоритм это легко исправить.
Представьте, что у вас есть входные данные 10,8,8,7. Вы продаете 10, продаете 9, которые становятся, вы продаете 8, которые становятся - пока все хорошо. Но не стоит продавать семерку, которой она стала. Понять это очень легко: вам нужно только посмотреть на следующий товар в магазине, и вам нужно только вспомнить, что у вас в магазине останется n-ное количество семерок, как только у вас закончатся восьмерки.
Однако это немного кодирования, и есть простой выход, который, вероятно, приемлем: просто удалите номер из хранилища и добавьте его обратно. Удаление одного элемента из TreeSet — это O(log n); так же добавляет. Мы собираемся добавлять/удалять m предметы, где m — это «вы можете продать ЭТО много предметов», которое также ограничено 10^5, что является довольно небольшим числом, так что это O(m * log n). Предполагая, что n и m имеют одинаковую общую величину (все это не более 10^5, как вы это объяснили), тогда O(n*log n) отсортируем его и O(n*log n) запустим алгоритм; постоянные факторы не учитываются, так что это O(n * log n + n * log n), то есть O(2 * n * log n), что есть... просто еще раз O(n log n): алгоритм стал медленнее только из-за постоянного фактора, который не имеет значения (чтобы сделать это реалистичным: потому что вы можете просто бросить несколько центов в IAAS поставщик более быстрого процессора, который будет опережать постоянный коэффициент. Вы не можете сделать это для алгоритма, который, например, O(n^3) (при добавлении большего количества входных данных для решения проблемы время, необходимое для ее решения, будет выглядеть как кривая y = x^3 с включенным «размером входных данных»). ось X и «затраченное время» на Y, и если вы посмотрите на y = x^3, это очень крутая линия — деньги не могут бороться с этой линией. Они могут бороться с y = x или, в данном случае, y = 1.
Собрать это вместе предельно тривиально:
List<Integer> itemsAvailable = readThisFromTheInput();
int sales = readThisAsWell();
var items = new TreeSet<Integer>(itemsAvailable);
long answer = 0;
for (int i = 0; i < sales; i++) {
int v = items.pollLast();
answer += v;
items.add(v - 1);
}
System.out.println(answer);
(TreeSet сохраняет свое содержимое отсортированным по своей сути.)
Спасибо за такое подробное объяснение! Однако я попробовал запустить ваш код и не получил ожидаемых результатов. Кажется, что TreeSet поддерживает только один экземпляр каждого значения. Итак, в приведенном мной примере вместо 55 получилось 10+9+8+7+6+5=45.
@user3525805 user3525805 да, хорошая мысль, вам нужен Multiset guava или используйте HashMap<Integer, Integer>, который сопоставляет значение с количеством товаров в магазине с этим значением количества на складе.
Если вы измените TreeSet на PriorityQueue(Comparator.reversed()), а затем добавите коллекцию, она должна работать должным образом. Все остальное должно быть идентичным согласно моему тесту. Используйте PriorityQueue.poll() для получения предмета.
Вот решение с использованием TreeMap. Это работает следующим образом.
revenue.item-1 или создайте новую запись, если она не существует.Включена дополнительная логика, которая генерирует исключение, если запасы превышают желаемые продажи. Можно также просто вернуть то, что было вычислено с доступными элементами. Это зависит от того, что требуется.
public static long getMaximumAmount(List<Integer> arr, int m) {
TreeMap<Integer, Integer> inventory = arr.stream()
.collect(Collectors.toMap(a -> a, b -> 1, Integer::sum,
() -> new TreeMap<>()));
long revenue = 0;
while (m > 0) {
if (inventory.isEmpty()) {
throw new RuntimeException("Insufficient items to meet sales");
}
Entry<Integer,Integer> e = inventory.lastEntry();
int item = e.getKey();
int remaining =e.getValue();
revenue += item;
if (e.getValue() == 1) { // remove it as
inventory.remove(item); // no more remaining
} else {
inventory.put(item, remaining-1); // update
}
// create new entry or increment existing
// don't update if item will be <= 0
if (item > 1) {
inventory.compute(item-1, (k,v)->v == null ? 1 : v+1);
}
m--;
}
return revenue;
}
Похоже, это работает для широкого диапазона значений и продаж менее чем за секунду.
Большое спасибо! Это работает очень хорошо и хорошо в рамках временных ограничений. Часть меня до сих пор задается вопросом, можно ли сделать это без какого-либо HashMap.
@user3525805 user3525805 Я только что подумал об этом. Используйте PriorityQueue. Смотрите мой комментарий в другом ответе.
«В приведенном примере было 5 товаров в количествах 10, 10, 8, 9 и 1. За 6 продаж максимальная сумма денег, которую он мог бы заработать, составила бы 10+10+9+9+9+8=55». Почему не 10+10+10+10+10+10=60?