Codility: MaxZeroProduct — проблемы сложности

Мое решение набрало 100% правильности, но 0% производительности. Я просто не могу понять, как минимизировать временную сложность.

Проблема:

Напишите функцию:

int solution(int A[], int N);

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

Например, если A = [7, 15, 6, 20, 5, 10], функция должна вернуть 3 (вы можете получить три нуля в конце, взяв произведение чисел 15, 20 и 10 или 20, 5 и 10) .

В другом примере, если A = [25, 10, 25, 10, 32], функция должна вернуть 4 (вы можете получить четыре нуля в конце, взяв произведение чисел 25, 25 и 32).

Предположим, что:

  • N — целое число в диапазоне [3..100 000];
  • каждый элемент массива A является целым числом в диапазоне [1..1 000 000 000].

Сложность:

  • ожидаемая временная сложность в наихудшем случае составляет O(N*log(max(A)));
  • ожидаемая сложность пространства в наихудшем случае составляет O (N) (не считая памяти, необходимой для входных аргументов).

Решение:

идея:

  1. разложить каждый элемент на пары пятерок и двоек
  2. суммировать каждые 3 пары в одну пару - это стоит O(N^3)
  3. найти пару, у которой минимальное значение координаты самое большое
  4. вернуть это минимальное значение координаты

код:

int solution(int A[], int N) {
    int fives = 0, twos = 0, max_zeros = 0;
    int(*factors)[2] = calloc(N, sizeof(int[2])); //each item (x,y) represents the amount of 5's and 2's of the corresponding item in A

    for (int i = 0; i< N; i++) {
        factorize(A[i], &fives, &twos);
        factors[i][0] = fives;
        factors[i][1] = twos;
    }

    //O(N^3)
    for (int i = 0; i<N; i++) {
        for (int j = i + 1; j<N; j++) {
            for (int k = j + 1; k<N; k++) {
                int x = factors[i][0] + factors[j][0] + factors[k][0];
                int y = factors[i][1] + factors[j][1] + factors[k][1];
                max_zeros = max(max_zeros, min(x, y));
            }
        }
    }
    return max_zeros;
}

void factorize(int val, int* fives, int* twos) {
    int tmp = val;  
    *fives = 0, *twos = 0;

    if (val == 0) return;    
    while (val % 5 == 0) { //factors of 5
        val /= 5;
        (*fives)++; 
    }
    while (val % 2 == 0) { //factors of 2
        val /= 2;
        (*twos)++;
    }
} 

Я не могу понять, как еще я могу перебирать массив размером N, чтобы найти оптимальные элементы 3 за время O(N*log(max(A))).


person Atalia.d    schedule 21.06.2018    source источник
comment
Привет, этот вопрос лучше подходит для проверки кода, так как он работает, но нуждается в улучшении алгоритмов.   -  person Antti Haapala    schedule 21.06.2018


Ответы (2)


Поскольку 2^30 > 1e9 и 5^13 > 1e9, существует ограничение в 30 * 13 = 390 различных пар множителей 2 и 5 в массиве, независимо от того, насколько велик массив. Это верхняя граница (фактическое число равно 213).

Отбросьте все, кроме трех представителей из массива для каждой пары, и тогда ваш алгоритм O (N ^ 3), вероятно, будет достаточно быстрым.

Если это все еще недостаточно быстро, вы можете продолжить, применяя динамическое программирование, вычисляя P[i,j], наибольшее произведение множителей 2s и 5s пар элементов с индексом ‹=i формы x * 2^y * 5^y+j (где x не делится ни на 2, ни на 5). Затем эту таблицу можно использовать во втором проходе динамического программирования, чтобы найти произведение трех чисел с наибольшим количеством нулей.

person Paul Hankin    schedule 21.06.2018
comment
Вероятно, они хотели чего-то другого, поскольку ожидаемая пространственная сложность в наихудшем случае равна O(N). - person Alexander Anikin; 21.06.2018
comment
@AlexanderAnikin Я не понимаю, почему этот ответ не удовлетворяет ограничениям сложности. Решение без DP использует пространство O (log N), а решение DP использует пространство O ((log N) ^ 3). Оба O (N). - person Paul Hankin; 21.06.2018
comment
Проблема в том, что ответ превышает ограничения сложности. - person Alexander Anikin; 22.06.2018
comment
да! это сработало. Я попробовал решение без DP. Согласно Codility, временная сложность составляла O(N*log(max(A))+log(max(A))**6). Спасибо! - person Atalia.d; 25.06.2018

В реальном мире мне не нравится такое метамышление, но все же мы сталкиваемся с какой-то искусственной проблемой с какими-то искусственными ограничениями...

Поскольку пространственная сложность равна O(N), мы не можем позволить себе динамическое программирование на основе исходных данных. Мы даже не можем составить карту N*факторов. В любом случае, мы можем позволить себе карту N*2, но в основном это все, что мы можем.

Поскольку временная сложность равна O(Nlog(max(A))), мы можем позволить себе разложить элементы на множители и выполнить простое одностороннее сокращение. Вероятно, мы можем сортировать элементы с помощью сортировки по счету - это больше похоже на Nlog^2(max(A)) для сортировки по 2 индексам, но большой O сравняет ее.

Если мое паучье чутье верно, мы должны просто выбрать что-то из этого массива counts и отшлифовать его с помощью массива 1-run-through. Что-то вроде того, что лучше всего считать 2, затем лучше всего 5, а затем мы можем перечислить остальные элементы, чтобы найти лучший общий продукт. Это просто эвристика, но размеры не лгут!

Просто мои 2 цента

person Alexander Anikin    schedule 21.06.2018