Как разделить множество на два подмножества так, чтобы разница между суммой чисел в двух наборах была минимальной? - PullRequest
47 голосов
/ 06 июля 2011

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

Это идея, которая у меня есть, ноЯ не уверен, что это правильное решение:

  1. Сортировать массив
  2. Взять первые 2 элемента.Рассмотрим их как 2 набора (каждый из которых имеет 1 элемент)
  3. Возьмите следующий элемент из массива.
  4. Решите, в каком наборе должен идти этот элемент (вычисляя сумму => она должна быть минимальной)
  5. Повтор

Это правильное решение?Можем ли мы сделать лучше?

Ответы [ 15 ]

0 голосов
/ 22 июня 2015

Это вариант задачи о ранце и сумме подмножеств. В задаче суммы подмножеств дано n натуральных чисел и значение k, и мы должны найти сумму подмножества, значение которого меньше или равно k. В приведенной выше задаче мы дали массив, здесь мы должны найти подмножество, сумма которого меньше или равна total_sum (сумма значений массива). Итак Сумма подмножества может быть найдена с помощью варианта алгоритма ранца, взятие прибыли в качестве заданных значений массива. И окончательный ответ total_sum-дп [п] [total_sum / 2]. Посмотрите на код ниже для ясности понимание.

#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
        int n;
        cin>>n;
        int arr[n],sum=0;
        for(int i=1;i<=n;i++)
        cin>>arr[i],sum+=arr[i];
        int temp=sum/2;
        int dp[n+1][temp+2];
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=temp;j++)
            {
                if(i==0 || j==0)
                dp[i][j]=0;
                else if(arr[i]<=j)
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-arr[i]]+arr[i]);
                else
                {
                dp[i][j]=dp[i-1][j];
                }
            }
        }
        cout<<sum-2*dp[n][temp]<<endl;
}
0 голосов
/ 13 июня 2015

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

  1. Сортируйте основной массив и разделите его на 2 команды.
  2. Затем начните делать команду равной по сдвигу и переставлять элементы из одного массива в другой, основываясь на условиях, упомянутых в коде.

Если разница состоит в том, что разница в сумме меньше минимального числа большего массива (массива с большей суммой), то смещение элементов из большего массива в меньший массив. Смещение происходит с условием , этот элемент из массива большего размера со значением, меньшим или равным разнице. Когда все элементы из массива большего размера больше разницы, смещение останавливается и происходит обмен. Я просто поменяю местами последние элементы массива (это можно сделать эффективнее, найдя, какие два элемента поменять местами), но все же это сработало. Дайте мне знать, если эта логика не удалась в любом сценарии

public class SmallestDifference {
static int sum1 = 0, sum2 = 0, diff, minDiff;
private static List<Integer> minArr1;
private static List<Integer> minArr2;
private static List<Integer> biggerArr;

/**
 * @param args
 */
public static void main(String[] args) {
    SmallestDifference sm = new SmallestDifference();
    Integer[] array1 = { 2, 7, 1, 4, 5, 9, 10, 11 };
    List<Integer> array = new ArrayList<Integer>();
    for (Integer val : array1) {
        array.add(val);
    }
    Collections.sort(array);
    CopyOnWriteArrayList<Integer> arr1 = new CopyOnWriteArrayList<>(array.subList(0, array.size() / 2));
    CopyOnWriteArrayList<Integer> arr2 = new CopyOnWriteArrayList<>(array.subList(array.size() / 2, array.size()));
    diff = Math.abs(sm.getSum(arr1) - sm.getSum(arr2));
    minDiff = array.get(0);
    sm.updateSum(arr1, arr2);
    System.out.println(arr1 + " : " + arr2);
    System.out.println(sum1 + " - " + sum2 + " = " + diff + " : minDiff = " + minDiff);
    int k = arr2.size();
    biggerArr = arr2;
    while (diff != 0 && k >= 0) {
        while (diff != 0 && sm.findMin(biggerArr) < diff) {
            sm.swich(arr1, arr2);
            int sum1 = sm.getSum(arr1), sum2 = sm.getSum(arr2);
            diff = Math.abs(sum1 - sum2);
            if (sum1 > sum2) {
                biggerArr = arr1;
            } else {
                biggerArr = arr2;
            }
            if (minDiff > diff || sm.findMin(biggerArr) > diff) {
                minDiff = diff;
                minArr1 = new CopyOnWriteArrayList<>(arr1);
                minArr2 = new CopyOnWriteArrayList<>(arr2);
            }
            sm.updateSum(arr1, arr2);
            System.out.println("Shifting : " + sum1 + " - " + sum2 + " = " + diff + " : minDiff = " + minDiff);
        }
        while (k >= 0 && minDiff > array.get(0) && minDiff != 0) {
            sm.swap(arr1, arr2);
            diff = Math.abs(sm.getSum(arr1) - sm.getSum(arr2));
            if (minDiff > diff) {
                minDiff = diff;
                minArr1 = new CopyOnWriteArrayList<>(arr1);
                minArr2 = new CopyOnWriteArrayList<>(arr2);
            }
            sm.updateSum(arr1, arr2);
            System.out.println("Swapping : " + sum1 + " - " + sum2 + " = " + diff + " : minDiff = " + minDiff);
            k--;
        }
        k--;
    }
    System.out.println(minArr1 + " : " + minArr2 + " = " + minDiff);
}

private void updateSum(CopyOnWriteArrayList<Integer> arr1, CopyOnWriteArrayList<Integer> arr2) {
    SmallestDifference sm1 = new SmallestDifference();
    sum1 = sm1.getSum(arr1);
    sum2 = sm1.getSum(arr2);
}

private int findMin(List<Integer> biggerArr2) {
    Integer min = biggerArr2.get(0);
    for (Integer integer : biggerArr2) {
        if(min > integer) {
            min = integer;
        }
    }
    return min;
}

private int getSum(CopyOnWriteArrayList<Integer> arr) {
    int sum = 0;
    for (Integer val : arr) {
        sum += val;
    }
    return sum;
}

private void swap(CopyOnWriteArrayList<Integer> arr1, CopyOnWriteArrayList<Integer> arr2) {
    int l1 = arr1.size(), l2 = arr2.size(), temp2 = arr2.get(l2 - 1), temp1 = arr1.get(l1 - 1);
    arr1.remove(l1 - 1);
    arr1.add(temp2);
    arr2.remove(l2 - 1);
    arr2.add(temp1);
    System.out.println(arr1 + " : " + arr2);
}

private void swich(CopyOnWriteArrayList<Integer> arr1, CopyOnWriteArrayList<Integer> arr2) {
    Integer e;
    if (sum1 > sum2) {
        e = this.findElementJustLessThanMinDiff(arr1);
        arr1.remove(e);
        arr2.add(e);
    } else {
        e = this.findElementJustLessThanMinDiff(arr2);
        arr2.remove(e);
        arr1.add(e);
    }
    System.out.println(arr1 + " : " + arr2);
}

private Integer findElementJustLessThanMinDiff(CopyOnWriteArrayList<Integer> arr1) {
    Integer e = arr1.get(0);
    int tempDiff = diff - e;
    for (Integer integer : arr1) {
        if (diff > integer && (diff - integer) < tempDiff) {
            e = integer;
            tempDiff = diff - e;
        }
    }
    return e;
}

}

0 голосов
/ 15 марта 2014
int ModDiff(int a, int b)
{
    if(a < b)return b - a;
    return a-b;
}

int EqDiv(int *a, int l, int *SumI, int *SumE)
{
    static int tc = 0;
    int min = ModDiff(*SumI,*SumE);
    for(int i = 0; i < l; i++)
    {
            swap(a,0,i);
            a++;
            int m1 = EqDiv(a, l-1, SumI,SumE);
            a--;
            swap(a,0,i);

            *SumI = *SumI + a[i];
            *SumE = *SumE - a[i];
            swap(a,0,i);
            a++;
            int m2 = EqDiv(a,l-1, SumI,SumE);
            a--;
            swap(a,0,i);
            *SumI = *SumI - a[i];
            *SumE = *SumE + a[i];

            min = min3(min,m1,m2);

    }
    return min;

}

вызовите функцию с SumI = 0 и SumE = сумма всех элементов в.Это O (n!) Решение вычисляет способ, которым мы можем разделить данный массив на 2 части, так что разница минимальна.Но определенно не практично из-за n!временная сложность, стремящаяся улучшить это, используя DP.

0 голосов
/ 06 июля 2011

Вы сортируете свое подмножество в порядке убывания или в порядке возрастания?

Подумайте об этом так: массив {1, 3, 5, 8, 9, 25}

, если выесли бы разделить, вы бы получили {1,8,9} = 18 {3,5,25} = 33

Если бы они были отсортированы по убыванию, то получилось бы намного лучше

{25,1} = 26 {9,8,5,3} = 25

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

РЕДАКТИРОВАТЬ: Читайте пост ЦкуцциУ меня не работает

0 голосов
/ 06 июля 2011

Одно небольшое изменение: обратный порядок - начните с наибольшего числа и уменьшите.Это минимизирует ошибку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...