将数组拆分为两个子阵列,总和最小

时间:2013-12-03 11:28:15

标签: java arrays algorithm math

我的问题是,如果给定一个数组,我们必须将它分成两个子数组,这样两个数组之和的绝对差值是最小的,条件是数组的元素数量之间的差异应该是至少一个。

让我举个例子。假设

实施例1:100 210 100 75 340

答案:

Array1 {100,210,100}和Array2 {75,340} - >差异= | 410-415 | = 5

实施例2:10 10 10 10 40

答案:Array1 {10,10,10}和Array2 {10,40} - >差异= | 30-50 | = 20

在这里我们可以看到虽然我们可以将数组划分为{10,10,10,10}和{40},但我们没有划分,因为约束“数组之间的元素数量应该是1”如果我们这样做就会受到侵犯。

有人能为此提供解决方案吗?

我的方法:

- >计算数组之和

- >将总和除以2

- >让背包的大小= sum / 2

- >将数组值的权重视为1.(如果遇到背包问题,您可能知道权重概念)

- >然后将数组值视为权重值。

- >计算答案,即array1 sum。

- >总和 - 答案=数组2总和

这种方法失败了。

计算两个数组之和就足够了。我们对哪些元素构成总和不感兴趣。

谢谢!

资料来源:这是ICPC的问题。

3 个答案:

答案 0 :(得分:2)

我有一个在O(n 3 )时间内工作的算法,但我没有硬性证据证明它是最优的。它似乎适用于我给它的每个测试输入(包括一些负数),所以我认为它值得分享。

首先将输入分成两个大小相等的数组(称为one[]two[]?)。从one[0]开始,看看two[]中的哪个元素会为您提供最佳结果如果已交换。无论哪一个给出最好的结果,交换。如果没有给出更好的结果,请不要交换它。然后转到one[]中的下一个元素并再次执行。

那部分本身就是O( 2 )。问题是,第一次可能无法获得最佳效果。如果你只是继续这样做,直到你不再进行交换,你最终会得到一个丑陋的泡泡型结构,这使得它总共为O(n 3 )。

这里有一些丑陋的Java代码要展示(如果你想使用它,也可以在ideone.com):

static int[] input = {1,2,3,4,5,-6,7,8,9,10,200,-1000,100,250,-720,1080,200,300,400,500,50,74};

public static void main(String[] args) {    
    int[] two = new int[input.length/2];
    int[] one = new int[input.length - two.length];

    int totalSum = 0;
    for(int i=0;i<input.length;i++){
        totalSum += input[i];
        if(i<one.length)
            one[i] = input[i];
        else
            two[i-one.length] = input[i];
    }

    float goal = totalSum / 2f; 

    boolean swapped;
    do{
        swapped = false;
        for(int j=0;j<one.length;j++){
            int curSum = sum(one);
            float curBestDiff = Math.abs(goal - curSum);
            int curBestIndex = -1;

            for(int i=0;i<two.length;i++){
                int testSum = curSum - one[j] + two[i];
                float diff = Math.abs(goal - testSum);
                if(diff < curBestDiff){
                    curBestDiff = diff;
                    curBestIndex = i;
                }
            }

            if(curBestIndex >= 0){  
                swapped = true;
                System.out.println("swapping " + one[j] + " and " + two[curBestIndex]);
                int tmp = one[j];
                one[j] = two[curBestIndex];
                two[curBestIndex] = tmp;
            }
        }
    } while(swapped);

    System.out.println(Arrays.toString(one));
    System.out.println(Arrays.toString(two));
    System.out.println("diff = " + Math.abs(sum(one) - sum(two)));
}

static int sum(int[] list){
    int sum = 0;
    for(int i=0;i<list.length;i++)
        sum += list[i];
    return sum;
}

答案 1 :(得分:0)

您能否提供有关输入上限的更多信息?

对于你的算法,我认为你正试图选择楼层(n / 2)项目并找到它的最大值总和为array1 sum ...(如果这不是你原来的想法那么请忽略以下几行)< / p>

如果是这种情况,那么背包大小应为n / 2而不是sum / 2,

但即便如此,我认为它仍然无效。 ans是min(|a - b|),最大化a是另一个问题。例如,{2,2,10,10},你会得到a = 20,b = 4,而ans是a = b = 12。

要回答这个问题,我想我需要更多关于输入上限的信息。 我无法想出一个出色的dp状态,而是一个三维状态

dp(i,n,v) := in first i-th items, pick n items out and make a sum of value v

每个州都是0或1(假或真)

dp(i,n,v) = dp(i-1, n, v) | dp(i-1, n-1, v-V[i])

这种dp状态太天真了,它的复杂度非常高,通常无法通过ACM / ICPC问题,所以如果可能的话请提供更多信息,看看我是否可以提出另一个更好的解决方案......希望我能提供帮助有点:)

答案 2 :(得分:0)

DP解决方案将给出lg(n)时间。两个数组,从开始到结束迭代一个,并计算总和,另一个迭代从结束到开始,并做同样的事情。最后,从开始到结束迭代并获得最小的差异。