我的问题是,如果给定一个数组,我们必须将它分成两个子数组,这样两个数组之和的绝对差值是最小的,条件是数组的元素数量之间的差异应该是至少一个。
让我举个例子。假设
实施例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的问题。
答案 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)时间。两个数组,从开始到结束迭代一个,并计算总和,另一个迭代从结束到开始,并做同样的事情。最后,从开始到结束迭代并获得最小的差异。