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

Это жесткий проблема алгоритмов, что :

разделите список на 2 части (sum), чтобы их сумма была ближе всего друг к другу

длина списка равна 1

например : 23 65 134 32 95 123 34

1.sum = 256

2.sum = 250

1.список = 1 2 3 7

2.список = 4 5 6

У меня есть алгоритм, но он не работа для всех входов.

  1. init. списки list1 = [], list2 = []
  2. элементами сортировки (по заданному списку) [23 32 34 65 95 123 134]
  3. поп последний (МАКС один)
  4. вставить в список, который отличается менее

реализация : list1 = [], list2 = []

  1. выберите 134 вставить list1. list1 = [134]
  2. выберите 123 вставить list2. потому что, если вы вставляете в list1, разница становится больше
    3. выберите 95 и вставьте list2 . потому что sum(list2) + 95 - sum (list1) меньше.

и так далее...

3 ответов


проблема в NPC, но для нее есть псевдополиномиальный алгоритм, это 2-раздел проблема, вы можете следовать пути псевдо-полиномиального алгоритма времени для sub set sum проблема для решения этой проблемы. Если входной размер полиномиально связан с входными значениями, то это можно сделать за полиномиальное время.

в вашем случае (Весы суммы

пусть Sum = сумма весов, мы необходимо создать двумерный массив A, а затем построить A, столбец за столбцом

A[i, j] = true if (j == weight[i] или j - weight[i] = weight[k] (k находится в списке)).

создание массива с помощью этого алгоритма занимает O (n^2 * sum/2).

наконец, мы должны найти самый ценный столбец, который имеет истинное значение.

вот пример:

элементов:{0,1,2,3} весы: {4,7,2,8} => sum = 21 sum/2 = 10

items/weights 0|  1  | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10    
  --------------------------------------------------------- 
  |0             |  0  | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0
  |1             |  0  | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0
  |2             |  0  | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1
  |3             |  0  | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1

Итак, потому что a[10, 2] == настоящий раздел 10, 11

это алгоритм, который я нашел здесь и отредактировал немного, чтобы решить вашу проблему:

bool partition( vector< int > C ) {
 // compute the total sum
 int n = C.size();
 int N = 0;
 for( int i = 0; i < n; i++ ) N += C[i];
 // initialize the table 
 T[0] = true;
 for( int i = 1; i <= N; i++ ) T[i] = false;
 // process the numbers one by one
 for( int i = 0; i < n; i++ )
  for( int j = N - C[i]; j >= 0; j--)
   if( T[j] ) T[j + C[i]] = true;

 for(int i = N/2;i>=0;i--)
    if (T[i])
      return i;
 return 0;
}

Я только что вернул первый T[i], который является истинным вместо возврата T[N/2] (в порядке max to min).

найти путь, который дает это значение не трудно.


вы можете переформулировать это как проблема с рюкзаком.

У вас есть список предметов с общим весом M, которые должны быть установлены в бункер, который может содержать максимальный вес M/2. Предметы, упакованные в корзину, должны весить как можно больше, но не больше, чем в корзине.

для случая, когда все веса неотрицательны, эта проблема только слабо NP-complete и имеет полиномиальные временные решения.

описание динамических программные решения для этой проблемы можно найти на Википедия.


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

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

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

генерация всех разделов может быть выполнена путем рассмотрения двоичного представления каждого целого числа от 0 до 2^n, где каждая двоичная цифра определяет, находится ли соответствующий элемент в левом или правом разделе.