Определите, может ли массив чисел быть разделен на два массива, каждый из которых содержит одинаковую сумму чисел

Ниже приведен код для определения того, может ли массив чисел быть разделен на два массива, причем каждый массив содержит одинаковую сумму чисел. например: {1, 3 ,2, 6} можно разделить на {6} и {1,2,3}, поэтому возвращаем true хотя {1,5,7} нельзя разделить на два, сбалансированный массив, поэтому верните false

public boolean canBalance(int[] nums) {
    for (int i = 0; i < nums.length; i++) { 
       int sum = 0;
       for (int j = 0; j < i; j++) sum += nums[j];
       for (int j = i; j < nums.length; j++) sum -= nums[j];
       if (sum == 0) return true;    
    }    
    return false;
}

это принятый ответ для упражнения в codingbat, я не понимаю эту часть в частности:

for (int j = 0; j < i; j++) sum += nums[j];
for (int j = i; j < nums.length; j++) sum -= nums[j];

не для итерация обычно начинается с { и заканчивается } ? и почему, если sum == 0 означает, что он может быть сбалансирован? Я попытался записать его на листе бумаги с массивом {1,3,2,6} и имел сумму 26, которая возвращает false, где очевидно, что {1,3,2,6} должен вернуть true.

Я думаю, я неправильно понял код, я не знаю, какую. Или, может быть, алгоритм ложен, но он был принят в codingbat

8 ответов


два for-loops предназначены для взвешивания двух частей массива, чтобы найти точка балансировки массива массива.

подумайте об этом так:

у вас есть пустая шкала баланса, на первой итерации внешнего цикла for, i равен нулю.

сначала для цикла, здесь j-0, а i-0 i < j ложь, таким образом, он не входит в первый for-loop и переходит во второй for-loop и вычитает все числа из сумма.

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

на рисунках это похоже на начало с пустой шкалы баланса, добавление всех элементов во вторую шкалу и перемещение по одному элементу в первую шкалу, например:

enter image description here

В конце концов, если сумма равна нулю, то массив можно сбалансировать, поэтому верните истинный. Если сумма не равна 0, она несбалансирована.

значения в уравновешиваются циклами следующим образом:

итерация внешнего цикла for, когда i равно 0
Цикл 2 - > i(0) j (0) вычесть 1, сумма равна -1
Цикл 2 - > i(0) j (1) вычесть 3, сумма равна -4
Петля 2 - > i(0) j (2) вычесть 2, сумма равна -6
Цикл 2 - > i(0) j (3) вычесть 6, сумма -12

итерация внешнего цикла for, когда i равно 1
Петля 1 - > i(1) j (0) добавить 1, сумма равна 1
Цикл 2 - > i(1) j (1) вычесть 3, сумма равна -2
Петля 2 - > i(1) j (2) вычесть 2, сумма равна -4
Цикл 2 - > i(1) j (3) вычесть 6, сумма -10

итерация внешнего цикла for, когда i равно 2
Цикл 1 - > i(2) j (0) добавить 1, сумма равна 1
Цикл 1 - > i(2) j (1) Добавить 3, сумма равна 4
Цикл 2 - > i(2) j (2) вычесть 2, сумма равна 2
Петля 2 - > i(2) j (3) вычесть 6, сумма равна -4

итерации внешнего для цикла, когда i равно 3
Цикл 1 - > i(3) j (0) добавить 1, сумма равна 1
Цикл 1 - > i(3) j (1) Добавить 3, сумма равна 4
Цикл 1 - > i(3) j (2) Добавить 2, сумма равна 6
Цикл 2 - > i (3) j(3) вычесть 6, сумма равна 0

конечный результат истинен, поэтому массив может быть сбалансирован

код:

public class Test {

    public static void main(String[] args) {
        int[] test = { 1, 3, 2, 6 };
        System.out.println("\nFinal result is "+canBalance(test));
    }

    public static boolean canBalance(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            System.out.println("\nIteration of outer for loop when i is " + i);
            int sum = 0;
            for (int j = 0; j < i; j++){
                sum += nums[j];
                System.out.println("Loop 1 -> i(" +i + ") j("+j + ") Add "+nums[j] + ", sum is "+sum+"       ");
            }
            for (int j = i; j < nums.length; j++){
                sum -= nums[j];
                System.out.println("Loop 2 -> i(" +i + ") j("+j + ") Subtract "+nums[j] + ", sum is "+sum+"       ");
            }
            if (sum == 0)
                return true;
        }
        return false;
    }
}

если вы хотите разрешить перетасовки между элементами массива, вы можете использовать рекурсию следующим образом (комментарии пояснений)

public class Test {

    public static void main(String[] args) {
        int[] original = { 10, 2, 24, 32 };
        System.out.println(canDivideArray(original));
    }

    private static boolean canDivideArray(int[] originalArray) {
        int total = 0;

        for (int number : originalArray) {
            total += number;
        }

        // check if sum == 2x for any value of x
        if (total % 2 != 0) {
            return false;
        } else {
            // sum of each half array should be x
            total /= 2;
        }
        return isTotal(originalArray, originalArray.length, total);
    }

    private static boolean isTotal(int array[], int n, int total) {
        // successful termination condition
        if (total == 0) {
            return true;
        }

        // unsuccessful termination when elements have finished but total is not reached
        if (n == 0 && total != 0){
            return false;
        }

        // When last element is greater than total
        if (array[n - 1] > total)
            return isTotal(array, n - 1, total);

        //check if total can be obtained excluding the last element or including the last element 
        return isTotal(array, n - 1, total - array[n - 1]) || isTotal(array, n - 1, total); 
    }

}

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

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

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

public boolean canBalance(int[] nums) {
  int sumL = 0, sumR = 0;
  int l = -1, r = nums.length;
  while (r - l > 1) {
    if (sumL < sumR) {
      sumL += nums[++l];
    } else {
      sumR += nums[--r];
    }
  }
  return sumL == sumR;
}

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

вот моя (рекурсия), которая определяет только, есть ли место для разделения массива так, чтобы сумма чисел на одной стороне была равна сумме чисел на другой стороне. Беспокойство об indexOutOfBounds, которое может легко произойти в рекурсии, небольшая ошибка может оказаться фатальной и дать много исключений и ошибок.

public boolean canBalance(int[] nums) {
  return (nums.length <= 1) ? false : canBalanceRecur(nums, 0);   
}
public boolean canBalanceRecur(int[] nums, int index){ //recursive version
  if(index == nums.length - 1 && recurSumBeforeIndex(nums, 0, index) 
  != sumAfterIndex(nums, index)){ //if we get here and its still bad
  return false;
  }
  if(recurSumBeforeIndex(nums, 0, index + 1) == sumAfterIndex(nums, index + 1)){
  return true;
  }
  return canBalanceRecur(nums, index + 1); //move the index up
}
public int recurSumBeforeIndex(int[] nums, int start, int index){
   return (start == index - 1 && start < nums.length) 
   ? nums[start] 
   : nums[start] + recurSumBeforeIndex(nums, start + 1, index);
}

public int sumAfterIndex(int[] nums, int startIndex){
  return (startIndex == nums.length - 1) 
  ? nums[nums.length - 1] 
  : nums[startIndex] + sumAfterIndex(nums, startIndex + 1);
}

//не рекурсивно

public boolean canBalance(int[] nums) {
   for(int a = 0; a < nums.length; a++) {
      int leftSum = 0;
      int rightSum = 0; 
      for(int b = 0; b < a; b++) {
      leftSum += nums[b];
      }
      for(int c = a; c < nums.length; c++) {
      rightSum += nums[c];
      }
      if(leftSum == rightSum) {
      return true;
      }
   }
   return false;
}

Я думаю, что "разделение" в исходном вопросе не позволяет переупорядочить массив. Итак, вы просто ломаете массив в определенной позиции, и у нас будет левая и правая стороны массива. Числа в каждой стороне должны иметь одинаковую сумму.

индекс (i) внешней петли-это положение разрыва.

for (int i = 0; i < nums.length; i++) {

первый внутренний цикл суммирует левую сторону массива.

for (int j = 0; j < i; j++) sum += nums[j];

второй внутренний цикл вычитает элементы права сторона от суммы левого массива.

for (int j = i; j < nums.length; j++) sum -= nums[j];

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


подход к этой проблеме с решением DP.

/ / создайте 2D-массив и заполните его снизу вверх //DP[totalsum / 2 + 1] [длина массива +]

bool divisible(int arr[], int size)
{
int sum = 0;

// Determine the sum 
for (i = 0; i < size; i++) sum += arr[i];

if (sum%2 != 0) return false;

bool DP[sum/2+1][size+1];

// initialize top row as true
for (i = 0; i <= size; i++)
    DP[0][i] = true;

// initialize leftmost column, except DP[0][0], as 0
for (i = 1; i <= sum/2; i++)
    DP[i][0] = false;     

 // Fill the partition table in botton up manner 
 for (int i = 1; i <= sum/2; i++)  
 {
     for (int j = 1; j <= size; j++)  
     {
         DP[i][j] = DP[i][j-1];
         if (i >= arr[j-1])
             DP[i][j] = DP[i][j] || DP[i - arr[j-1]][j-1];
     }        
 }    
 return DP[sum/2][size];
}     

Как @Alvin Bunk сказал в комментарии, ответ, который вы даете в самом вопросе, не является хорошим ответом, он работает по-другому, даже если порядок элементов изменяется в массиве.

вы должны проверить эту Вики для теории и реализовать ее:http://en.wikipedia.org/wiki/Partition_problem


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

//Create a method that gets the sum from start to the iteration before end.
public int sumArray (int[] nums, int start, int end)
{
  //Create a sum int that tracks the sum.
  int returnSum = 0;
  //Add the values from start (inclusive) to end (exclusive). In other words i : [start, end)
  for (int i = start; i < end; i++)
  {
    returnSum += nums[i];
  }
  return returnSum;
}

//This is our main class.
public boolean canBalance(int[] nums) {
  //If nums has an actual value, we can work with it.
  if (nums.length > 0)
  {
    //We check to see if there is a value that is equal by using the sumArray method.
    for (int i = 0; i < nums.length; i++)
    {
      //If from [0,i) the value equals from [i, nums.length), we return true;
      if (sumArray(nums, 0, i) == sumArray(nums, i, nums.length))
      {
        return true;
      }
    }
    //If we finish the loop, and find nothing, we return false;
    return false;
  }
  //If there is no value, we return false.
  else
  {
    return false;
  }
}

когда вокруг кода после оператора for в Java нет фигурных скобок, следующая строка является единственной строкой, обрабатываемой как часть оператора for. В этом случае for-это функция, вызывающая следующую строку кода, которая является оператором for, вызывающим следующую строку кода. Таким образом, первый для вызовов второй, для которого оценивается, чтобы увидеть, являются ли две стороны одинаковыми, и если они не являются, то он возвращается ко второму, для которого продолжает увеличивать себя пока он не закончит, а затем возвращается к первому for, который увеличивается и вызывает второй for... и так далее. Этот код кажется частично сломанным, поскольку он должен иметь все числа в числовом порядке, и он ничего не проверяет посередине.

например:
{1, 2, 3, 1} //evaluates to true because 1-1=0, although it should be false
{6, 2, 2, 3} //evaluates to true because 6-3-2=0, although it should be false
{2, 3, 4, 6} //evaluates to true because 2+3-6=0, although it should be false