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

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

Представьте себе жадного продавца, у которого есть куча товаров и определенное количество каждого предмета. Он продает каждый товар по цене, равной его оставшемуся количеству, и хочет заработать как можно больше денег за определенное количество продаж. Я думаю, что количество предметов, количество каждого предмета и количество продаж могут достигать 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;
}

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

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

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

«В приведенном примере было 5 товаров в количествах 10, 10, 8, 9 и 1. За 6 продаж максимальная сумма денег, которую он мог бы заработать, составила бы 10+10+9+9+9+8=55». Почему не 10+10+10+10+10+10=60?

Robby Cornelissen 26.08.2024 03:04

Поскольку, за исключением последней попытки, ваша программа работала, похоже, об этом лучше спросить в Code Review . Пожалуйста, прочитайте их Как задать вопрос. Включите в вопрос код для вашей лучшей попытки.

Old Dog Programmer 26.08.2024 03:06

Этот вопрос следует открыть вновь. Речь идет не о Code Review: код был сломан — он неправильно делал то, что требовалось. В данном конкретном случае требование включает в себя производительность. Алгоритмическая сложность полностью соответствует тому, для чего нужен SO.

rzwitserloot 26.08.2024 03:12

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

rzwitserloot 26.08.2024 03:13

Это могло бы прояснить ситуацию, но поставленный вопрос достаточно ясен; На вопрос Робби Корнелиссена можно легко ответить, просто внимательно прочитав и добавив немного воображения: Конечно, упражнение предназначено для того, чтобы вы разузнали: «Если владелец магазина продает предмет, это означает, что у владельца магазина теперь на 1 такого предмета меньше». Полностью разочарован голосованием за закрытие здесь.

rzwitserloot 26.08.2024 03:14

Немного жаль, что ответ пропадет даром. @user352805 - он здесь pastebin.com/9va3Z804 - ждет повторного открытия.

rzwitserloot 26.08.2024 03:23

@rzwitserloot Значит, цена товара снижается по мере уменьшения его оставшегося количества? Это интерпретация?

Robby Cornelissen 26.08.2024 03:46

@RobbyCornelissen Да, именно об этом говорится в вопросе, и это соответствует приведенному примеру. Если бы я мог поговорить с автором этого вопроса, я бы посоветовал им переписать его; «Это магазин» не согласуется с «цена предмета зависит от его количества» - на самом деле, мы интуитивно понимаем цены наоборот (чем более редкий предмет, тем выше должна быть цена). Но поставленный вопрос не является двусмысленным. Это действительно правильная интерпретация.

rzwitserloot 26.08.2024 04:00

Я бы проголосовал за повторное открытие, если бы ОП отредактировал вопрос, чтобы показать некоторый код... В противном случае не стоит стрелять в темноте.

MarsAtomic 26.08.2024 04:12

@MarsAtomic Это вопрос алгоритмической сложности; ни одной строчки из того, что они написали, в «фиксе» не останется.

rzwitserloot 26.08.2024 04:16

@RobbyCornelissen Да, цена всегда равна оставшемуся количеству. Таким образом, цена снижается на один каждый раз, когда он продается. Я добавил слово в свое описание, чтобы, надеюсь, прояснить это.

user3525805 26.08.2024 06:10

Я тоже уже проголосовал за открытие. Просто жду еще одного голосования.

Robby Cornelissen 26.08.2024 06:36

@rzwitserloot, за изысканную простоту вашего решения вы заслуживаете медали и спасибо за объяснение.

Marce Puente 26.08.2024 14:45
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
14
92
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

сортировка списка целых чисел 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 26.08.2024 19:42

@user3525805 user3525805 да, хорошая мысль, вам нужен Multiset guava или используйте HashMap<Integer, Integer>, который сопоставляет значение с количеством товаров в магазине с этим значением количества на складе.

rzwitserloot 26.08.2024 19:45

Если вы измените TreeSet на PriorityQueue(Comparator.reversed()), а затем добавите коллекцию, она должна работать должным образом. Все остальное должно быть идентичным согласно моему тесту. Используйте PriorityQueue.poll() для получения предмета.

WJS 26.08.2024 21:08
Ответ принят как подходящий

Вот решение с использованием TreeMap. Это работает следующим образом.

  • вычислить частоту всех элементов.
  • затем возьмите последний предмет (самый дорогой) с карты.
  • добавляй в revenue.
  • измените частоту появления этого товара на 1, чтобы уменьшить количество доступных товаров по этой цене.
  • если (это 1, удалите его) с карты.
  • теперь увеличьте частоту item-1 или создайте новую запись, если она не существует.
  • продолжать до тех пор, пока m == 0

Включена дополнительная логика, которая генерирует исключение, если запасы превышают желаемые продажи. Можно также просто вернуть то, что было вычислено с доступными элементами. Это зависит от того, что требуется.

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 26.08.2024 20:24

@user3525805 user3525805 Я только что подумал об этом. Используйте PriorityQueue. Смотрите мой комментарий в другом ответе.

WJS 26.08.2024 21:11

Другие вопросы по теме