Найдите максимум из всех минимальных сумм

Даны целочисленные массивы A и B размера n.

Найдите все возможные комбинации (подмножества) размера 1, 2, 3, ..., n. Мы получаем 2 степени n возможностей

Для каждой комбинации (подмножества) A найдите минимум в этом подмножестве, скажем min, и умножьте это значение на сумму элементов для этой комбинации из массива B.

Найдите максимальное значение из всех вышеуказанных минимумов. Верните результат по модулю (10^9+7).

Ограничения:

n in range 1 to 10^5
1 <= A[i], B[i] <= 10^6

Пример:

A = [1,1,3]
B = [1,2,2]

indices:  A      min         B    sum         min*sum
                 (subset A)       (subset B)
--------------------------------------------------------------
[0]       1       1          1       1        1*1=1
[1]       1       1          2       2        1*2=2
[2]       3       3          2       2        3*2=6
[0,1]     1,1     1          1,2     3        1*3=3
[0,2]     1,3     1          1,2     3        1*3=3
[1,2]     1,3     1          2,2     4        1*4=4
[0,1,2]   1,1,3   1          1,2,2   5        1*5=5

Result = max( all min*sum) = max(1,2,6,3,3,4,5) = 6

Вот мой код:

public static int solve(List<Integer> A, List<Integer> B) {
        int n = A.size();
        long max = 0;
    final int MOD = 1_000_000_007;
        // Loop through all subsets using bitmasking
        for (int mask = 0; mask < (1 << n); mask++) {
            int min = Integer.MAX_VALUE;
            long sum = 0;

            for (int i = 0; i < n; i++) {
                if ((mask & (1 << i)) != 0) {
                    min = Math.min(min, A.get(i));
                    sum += B.get(i);
                    sum %= MOD; // To prevent overflow
                }
            }
            long current = (int) ((long) min * sum % MOD);
            max = Math.max(max, current);
        }

        return (int) max;
     }

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

Каков правильный подход к решению этой проблемы?

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

Ответы 1

Ответ принят как подходящий

Вы можете попробовать все элементы в A как минимальное значение некоторой подпоследовательности A. Чтобы максимизировать произведение (min in A) * (sum in B) для фиксированного минимума, мы можем выбирать только элементы из B, чтобы добавить их к сумме, для которой элемент из A с соответствующим индексом не меньше этого фиксированного минимума. Кроме того, существует особый случай, когда элемент, соответствующий текущему элементу в A, который мы пытаемся использовать как минимум, всегда должен приниматься как минимум.

Затем мы можем выбрать оптимальные элементы, рассмотрев два случая (не пытаясь перепробовать все подмножества):

  1. Если рассматриваемый нами минимум положителен, то мы хотим взять как можно больше положительных соответствующих элементов из B.
  2. Если минимум отрицательный, то мы хотим взять в B как можно больше отрицательных элементов.

Обратите внимание, что случай минимума 0 может относиться к любому из случаев: произведение всегда будет 0.

Мы можем предварительно вычислить сумму всех положительных элементов в B с соответствующим элементом в A, превышающим определенное значение, с помощью префиксного массива сумм (после сортировки List индексов A в порядке убывания на основе значений в A). Мы можем сделать то же самое с отрицательными элементами.

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

Временная сложность: O(N log N)

public static int solve(List<Integer> A, List<Integer> B) {
    var sortedIndexesByA = IntStream.range(0, A.size()).boxed()
            .sorted(Comparator.comparingInt(A::get).reversed()).toList();
    long[] bSumPos = new long[B.size()], bSumNeg = new long[B.size()];
    for (int i = 0; i < B.size(); i++) {
        bSumPos[i] = (i > 0 ? bSumPos[i - 1] : 0) + 
                         Math.max(B.get(sortedIndexesByA.get(i)), 0);
        bSumNeg[i] = (i > 0 ? bSumNeg[i - 1] : 0) + 
                         Math.min(B.get(sortedIndexesByA.get(i)), 0);
    }
    long ans = Long.MIN_VALUE;
    for (int i = 0; i < A.size(); i++) {
        int low = 0, high = A.size() - 1;
        while (low <= high) {
            int mid = low + high >>> 1;
            if (A.get(sortedIndexesByA.get(mid)) < A.get(i))
                high = mid - 1;
            else
                low = mid + 1;
        }
        long sum = (A.get(i) > 0 ? bSumPos : bSumNeg)[high] + 
                   (A.get(i) > 0 ^ B.get(i) > 0 ? B.get(i) : 0);
        ans = Math.max(ans, sum * A.get(i));
    }
    return (int) (ans % 1000000007);
}

не уверен, предполагалось ли это в исходной загадке, однако ans = Math.max(ans, sum * A.get(i)) также страдает от целочисленного переполнения

Andrey B. Panfilov 09.06.2024 05:36

@AndreyB.Panfilov sum — это long, и такие задачи почти никогда не требуют целочисленных типов размером более 64 бит.

Unmitigated 09.06.2024 06:09

сумма не переполняется, умножение переполняется

Andrey B. Panfilov 09.06.2024 07:58

@AndreyB.Panfilov Я говорю, что, поскольку для решения задач обычно не требуются целочисленные типы размером более 64 бит, достаточно, чтобы умножение выполнялось как long.

Unmitigated 09.06.2024 08:06

Пожалуйста, просто рассмотрите два одинаковых набора: [5 из int.min_val]. Максимум будет: int.min_val x ((long) (int.min_val) x 5) — переполняется как int, так и long.

Andrey B. Panfilov 09.06.2024 08:33

Я пытаюсь понять этот ответ шаг за шагом и застрял в операторе We can precompute the sum of all positive elements in B with corresponding element in A larger than a particular value with a prefix sum array и соответствующем коде bSumPos[i] = (i > 0 ? bSumPos[i - 1] : 0) + Math.max(B.get(sortedIndexesByA.get(i)), 0); не могли бы вы добавить больше деталей или пример, чтобы понять эту часть логики?

Sid 11.06.2024 20:42

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

Sid 11.06.2024 21:12

@Sid Я сделал свой ответ более общим (обработка негативов), поскольку в вопросе не уточнялось. Короче говоря, bSumPos[i] хранит сумму всех положительных значений в B, где значение в A по тому же индексу больше или равно значению по индексу i в копии A с обратной сортировкой.

Unmitigated 11.06.2024 21:17

@AndreyB.Panfilov, для моей задачи подойдет длинный вариант, так как при вычислении значений я буду использовать модуль (10^9+7).

Sid 11.06.2024 21:17
bSumPos[i] stores the sum of all positive values in B where the value in A at the same index is greater or equal to the value at index i in a reverse sorted copy of A. Я ничего не понимаю, не могли бы вы объяснить на примере, как это помогает в решении этой проблемы.
Sid 11.06.2024 21:20

@Sid Допустим, мы пробуем какое-то значение x в A в качестве минимального значения. Тогда мы можем выбирать только другие элементы из A, которые больше или равны x, в качестве других элементов этого подмножества. Мы хотим выбирать только те элементы, которые увеличивают сумму в B, поэтому мы хотим брать только те элементы из A, у которых элемент в B имеет тот же индекс и имеет положительное значение. Вы это видите?

Unmitigated 12.06.2024 00:24

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