Оптимизация раздела массива

Я решал вопрос о проблеме программирования, но мое решение давало тайм-аут/ошибку для больших чисел. Может ли кто-нибудь помочь мне оптимизировать мое решение?

Вопрос:

You are given an array A of N integers. Now you are required to fixed X such that the difference between the following two values is minimum:

  1. A[1] * A[2] * A[3] * ......... * A[X]
  2. A[X+1] * A[X+2] * ........... * A[N]

and if there is more value of X then print the smallest one.

Constraint:

  • 1 <= 1 <= 10^5
  • 1 <= A[i] <= 10^18

Input:

  • The first line contains integer N (for size)
  • The second line contains space separated numbers (for array)
import java.util.*;
public class Main
{
    public static void main(String[] args) {
        Scanner s=new Scanner(System.in)
        int size=Integer.parseInt(s.nextLine);
        long arr[]=new long[size];
        for(int i=0;i<=size;i++){
            arr[i]=s.nextLong();
        }   
        long part1=1,part2=1;
        long diff=1;long minIndex=0;long minNo=0;
        
        for(int k=0;k<size-1;k++){
            part1=1;part2=1;
            //minIndex=k;
            for (int i=0;i<=k ; i++){
                part1=part1*arr[i];
            } 
            for(int j=k+1;j<=size;j++){
                part2=part2*arr[j];
            }
            //System.out.println(part1+"---"+part2);
            diff=Math.abs(part1-part2);
            if (k==0){
                minNo=diff;
                minIndex=k;
            }
            //System.out.println(diff);
            if (minNo>diff){
                
                 minNo=diff;
                 minIndex=k;
            }
               
            
        }
        System.out.println("MinNo: "+minNo+" Index: "+minIndex);
        
        
        
    }
}

Я тестировал этот ввод

5
9090909090909009 780009090900909 898989898898898 98998 9999776765576765

Ответ должен быть 2 (если считать с нуля, то 1), но мой код дает 4.

Вы берете произведение очень больших чисел. Они превысят максимальное значение long, поэтому вам нужно будет использовать BigInteger.

WJS 29.04.2019 00:22

Разве это не было частью конкурса, недавно проведенного на HackerEarth?

Andrew Scott 30.04.2019 13:25

Да, это так. Тот конкурс окончен.

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

Ответы 2

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

Нет необходимости снова и снова вычислять умножение подмассива. Это причина, по которой вы получаете ошибку тайм-аута. Вы используете Long для хранения умножения, что приведет к неправильному ответу, вам нужно использовать BigInteger. Ниже подход будет выполнять умножение только один раз. После этого вы можете просто повторить и выяснить различия между ними.

import java.math.BigInteger;
import java.util.Scanner;

public class ParitionArray {
    public static void main(String[] args){
        Scanner s=new Scanner(System.in);
        int size=Integer.parseInt(s.nextLine());
        long arr[]=new long[size];
        for(int i=0;i<size;i++){
            arr[i]=s.nextLong();
        }
        long minIndex=0;
        BigInteger minNo=BigInteger.ZERO;
        BigInteger[] prefixedMult = new BigInteger[size];
        prefixedMult[0] = BigInteger.valueOf(arr[0]);
        for(int k =1; k< size; k++){
            prefixedMult[k] = prefixedMult[k-1].multiply(BigInteger.valueOf(arr[k]));
        }

        for(int k=0;k<size;k++){
            BigInteger part1 = prefixedMult[k]; //multiplication of A[1]*A[2]A[3].........*A[k]
            BigInteger part2 = prefixedMult[size-1].divide(part1);    //multiplication of A[k+1]A[k+2]...........*A[size]

            BigInteger diff = part1.subtract(part2).abs();
            if (k==0){
                minNo=diff;
                minIndex=k;
            }
            //System.out.println(diff);
            if (minNo.compareTo(diff)==1){
                minNo=diff;
                minIndex=k;
            }
        }
        System.out.println("MinNo: "+minNo+" Index: "+minIndex);
    }
}

Вход:

5
2
8
6
5
3

Выход:

MinNo: 74 Index: 1

Я нашел ответ @AndrewScott полезным. Ниже приведена эквивалентная реализация на Java:

 public static void main(String[] args){
        Scanner s=new Scanner(System.in);
        int size=Integer.parseInt(s.nextLine());
        long arr[]=new long[size];
        for(int i=0;i<size;i++){
            arr[i]=s.nextLong();
        }
        long minIndex=0;
        Double minNo=Double.MAX_VALUE;
        Double[] prefixedMult = new Double[size];
        prefixedMult[0] = Math.log10((double)arr[0]);
        for(int k =1; k< size; k++){
            prefixedMult[k] = prefixedMult[k-1] + Math.log10((double)arr[k]);
        }

        for(int k=0;k<size;k++){
            Double part1 = prefixedMult[k]; //multiplication of A[1]*A[2]A[3].........*A[k]
            Double part2 = prefixedMult[size-1] - (part1);    //multiplication of A[k+1]A[k+2]...........*A[size]
            Double diff = Math.abs(part1 - part2);
            if (minNo > diff){
                minNo=diff;
                minIndex=k;
            }
        }
        System.out.println("MinNo: "+minNo+" Index: "+minIndex);
    }

Привет, спасибо за предложение bigInteger. С bigInteger я получаю правильный ответ. Но я не совсем понимаю ваш prefixedmult. Часы на переменной «diff» вернули 718 704 624 240 720, но это должно быть 718 74 81 477. Таким образом, окончательный результат должен быть MinNo: 74 Index: 1

Nandan Raj 30.04.2019 13:30

Какой вход вы примеряете?

mukesh210 30.04.2019 13:35

5(размер) 2 8 6 5 3

Nandan Raj 30.04.2019 13:38

Для такого небольшого ввода он должен работать даже в вашем коде.

Andrew Scott 30.04.2019 13:39

Я сомневаюсь, что для входных ограничений этот код будет работать

Andrew Scott 30.04.2019 13:40

Ты не имел в виду part2 = prefixedMult[size-1].divide(part1)?

tobias_k 30.04.2019 13:42

В такой задаче, как эта... обычно в конце есть условие разделить результат на 10^x перед отображением.

mukesh210 30.04.2019 13:52

@NandanRaj Была небольшая ошибка. Я исправил ее, и теперь она выдает правильный результат.

mukesh210 30.04.2019 13:58

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

tobias_k 30.04.2019 14:01

@MukeshPrajapati Спасибо, теперь работает

Nandan Raj 30.04.2019 14:03

Хотя ответ, предложенный @Mukesh Prajapati, работает, все же есть гораздо более быстрый и лучший способ сделать это.

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

Вы сохраняете совокупную сумму для быстрого поиска. Это позволяет быстро вычислить левую и правую суммы массива. Окончательная минимальная разница находится в ans, а индекс — в переменной index.

void partition(int n, vector<double> &a) {
    double total = 0; vector<double> sum_array_a;

    for(auto &x: a) {
            x = log10(x);
            total += x;
            sum_array_a.push_back(total);
    }

    double ans = INFINITY, index = -1;

    for(int i = 0; i < n; i++) {    // Check for all points if you can split here
            double left = sum_array_a[i];
            double right = total - left;    // Right side sum of elements
            double diff = abs(left - right);
            if (diff < ans) {
                    ans = diff;
                    index = i;
            }
    }

    printf("%f", index);

}

Привет, Андрей, я хочу знать, как реализовать это с помощью метода журнала.

Nandan Raj 30.04.2019 14:18

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

Как сделать полное двоичное дерево с 6 узлами?
Каким должно быть завершающее условие для этой реализации задачи с 15 головоломками?
Big-O Space Сложность вложенных операций
Как изменить задачу о резке стержня, чтобы взять размеры, которые увеличиваются более чем на единицу
Что имеется в виду под «частичным упорядочением» и «полным упорядочением» при обсуждении алгоритма синхронизации Лэмпорта?
Временная сложность для квадратного корня с использованием метода Ньютона
Умножение каждого элемента одного массива на каждый элемент другого массива и сортировка нового очень большого массива
Почему ошибка не соответствует оператору == при использовании `std::find`?
Удалите самый большой элемент из массива и добавьте половину его обратно в ту же позицию
Ошибка при поиске минимальной абсолютной разницы между любыми двумя парами элементов в массиве с использованием javascript