Я решал вопрос о проблеме программирования, но мое решение давало тайм-аут/ошибку для больших чисел. Может ли кто-нибудь помочь мне оптимизировать мое решение?
Вопрос:
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^51 <= A[i] <= 10^18Input:
- 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.
Разве это не было частью конкурса, недавно проведенного на HackerEarth?
Да, это так. Тот конкурс окончен.




Нет необходимости снова и снова вычислять умножение подмассива. Это причина, по которой вы получаете ошибку тайм-аута. Вы используете 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
Какой вход вы примеряете?
5(размер) 2 8 6 5 3
Для такого небольшого ввода он должен работать даже в вашем коде.
Я сомневаюсь, что для входных ограничений этот код будет работать
Ты не имел в виду part2 = prefixedMult[size-1].divide(part1)?
В такой задаче, как эта... обычно в конце есть условие разделить результат на 10^x перед отображением.
@NandanRaj Была небольшая ошибка. Я исправил ее, и теперь она выдает правильный результат.
Однако это может быть слишком медленным при многократном делении чисел, превышающих несколько тысяч цифр. Использование журнала, а затем сложение и вычитание, вероятно, лучше.
@MukeshPrajapati Спасибо, теперь работает
Хотя ответ, предложенный @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);
}
Привет, Андрей, я хочу знать, как реализовать это с помощью метода журнала.
Вы берете произведение очень больших чисел. Они превысят максимальное значение long, поэтому вам нужно будет использовать BigInteger.