给出一个数组,您必须找到最大可能的两个相等和,可以排除元素。
即1,2,3,4,6
被赋予数组
我们最多可以有两个等于6 + 2 = 4 + 3 + 1的和
即4,10,18,22, 我们可以得到两个相等的总和,即18 + 4 = 22
除了蛮力查找所有计算并检查两个可能的相等和之外,您将采用什么方法来解决此问题?
edit 1:数组元素的最大数目为N <= 50,每个元素最多可以为1 <= K <= 1000
edit 2:这是我的解决方法https://ideone.com/cAbe4g,在每种情况下,给定的时间限制为5秒,这会花费太多时间。
编辑3:-元素总数之和不能大于1000。
答案 0 :(得分:14)
我建议使用DP解决此问题,而不是跟踪A,B(两组的大小),而是跟踪A + B,A-B(两组的总和和差)。
然后针对数组中的每个元素,尝试将其添加到A或B中,或都不添加到两者中。
跟踪总和/差的优点是,您只需跟踪每个差的单个值,即针对该差看到的总和的最大值。
为了提高效率,我建议您从最小到最大顺序进行迭代,并在达到目前为止的最大差异后停止更新DP。
您也只能存储差异的绝对值,而忽略任何大于25000的差异(因为从这一点开始,差异不可能恢复为0)。
from collections import defaultdict
def max_equal_sum(E):
D=defaultdict(int) # Map from abs difference to largest sum
D[0]=0 # Start with a sum and difference of 0
for a in E: # Iterate over each element in the array
D2=D.copy() # Can keep current sum and diff if element is skipped
for d,s in D.items(): # d is difference, s is sum
s2 = s+a # s2 is new sum
for d2 in [d-a,d+a]: # d2 is new difference
D2[abs(d2)] = max(D2[abs(d2)],s2) # Update with largest sum
D=D2
return D[0]/2 # Answer is half the sum of A+B for a difference of 0
print max_equal_sum([1,2,3,4,6]) # Prints 8
print max_equal_sum([4,10,18,22]) # Prints 22
答案 1 :(得分:2)
值从0到1000的最大集合,其中没有两个等于和的子集,其中包含9个元素,例如:
{1, 2, 4, 8, 16, 32, 64, 128, 256, 512}
如果添加第十个元素,则它将等于前九个值的子集的总和。
如果在排除9个以上元素后发现两个总和相等的子集,则可以将被排除的元素中的两个相等总和相加,以形成更大的相等总和;这意味着您不应排除9个以上的元素。
被排除元素的总和,范围为0到1000。构建一个筛子以检查该范围内的哪些值可以用集合中的元素形成,最多需要50×1000步。 (我们可以存储加到每个总和上的最小数量的值,而不是布尔值,并使用该数量仅包括可以由9个或更少元素组成的总和。)
如果然后查看从小到大的排除数之和,则意味着查看从大到小的排除数之和。对于每个排除值的总和,我们检查哪些元素(最多9个)可以形成该总和(显然这不是一个简单的步骤,但我们知道元素的数量在筛子中存储的最小值与9之间),以及这给了我们一组排除的,因此也提供了一组包含的数字。然后,我们使用类似的筛分技术检查所包含的数字是否可以形成半数和;范围是1到500,最多可以进行50×500步。
在所有这一切中,当然要考虑奇数/偶数:如果总和为奇数,则必须排除具有奇数和的子集;如果总和为偶数,则仅需排除具有偶数和的子集。
我还没有真正弄清楚如何生成最坏情况的输入,因此很难判断最坏情况的复杂度。但我认为一般情况应该可行。
这里有一些活动内容。首先,筛子查找最多9个值的集合的总和,这些集合可以用20个总和为999的值排除(并具有正确的奇/偶性):
function excludedSums(values) {
var sieve = [0];
for (var i in values) {
var temp = [];
for (var j in sieve) {
if (sieve[j] == 9) continue;
var val = values[i] + Number(j);
if (!sieve[val] || sieve[j] + 1 < sieve[val]) {
temp[val] = sieve[j] + 1;
}
}
for (var j in temp) {
sieve[j] = temp[j];
}
}
var odd = values.reduce(function(ac, el) {return ac + el}, 0) % 2;
for (var i in sieve) {
if (Number(i) % 2 != odd) delete sieve[i];
}
return sieve;
}
var set = [40,7,112,15,96,25,49,49,31,87,39,8,79,40,73,49,63,55,12,70];
var result = excludedSums(set);
for (var i in result) document.write(i + ", ");
接下来,以一定的总和最多包含9个值的集合。在上面的示例中,我们看到了可以排除总和为99的一组或多组;让我们找出这些集合是什么:
function excludedSets(values, target) {
var sieve = [[[]]];
for (var i in values) {
var temp = [];
for (var j in sieve) {
var val = values[i] + Number(j);
if (val > target) continue;
for (var k in sieve[j]) {
if (sieve[j][k].length < 9) {
if (!temp[val]) temp[val] = [];
temp[val].push(sieve[j][k].concat([values[i]]));
}
}
}
for (var j in temp) {
if (!sieve[j]) sieve[j] = [];
for (var k in temp[j]) {
sieve[j].push(temp[j][k].slice());
}
}
}
return sieve[target];
}
var set = [40,7,112,15,96,25,49,49,31,87,39,8,79,40,73,49,63,55,12,70];
var result = excludedSets(set, 99);
for (var i in result) document.write(result[i] + "<br>");
(您会在输出中看到一些重复项,因为例如49值在集合中出现了3次。)
现在让我们测试没有排除值的集合是否可以一分为二。我们看到,可以形成例如99的和。值87和12,因此我们从集合中排除了这些值,并得到18个值的总和为900。现在,我们检查是否可以通过将集合中的值相加来形成一半总和450:
function sumPossible(values, target) {
var sieve = [true];
for (var i in values) {
var temp = [];
for (var j in sieve) {
var val = values[i] + Number(j);
if (val < target) temp[val] = true;
else if (val == target) return true;
}
for (var j in temp) sieve[j] = temp[j];
}
return false;
}
var set = [40,7,112,15,96,25,49,49,31,39,8,79,40,73,49,63,55,70];
document.write(sumPossible(set, 450));
因此450是该集合的可能一半和。显然,它不是最大的,因为我们随机抽取了总和99作为示例,而不是对所有总和从小到大进行迭代;实际上,第一个选项(不包括值7)将导致最大半数和496。
应该注意的是,集合越大,集合就越有可能被分成两半(如果它具有偶数和,或者如果具有奇数和,则去除了最小的奇数值)。用数百万个随机值集进行的测试,其总数之和最多为1000,这表明对于大于28的任何集大小,都不会出现无法将一半拆分的单个集的情况。(当然可以制作这样的集,例如49个和一个51个。)
答案 2 :(得分:0)
对于数组中的每个元素,存在三种可能性。 (i)将元素纳入第一组 (ii)将元素包含在第二组中 (iii)不包含任何元素 只要第一组和第二组的总和等于,就更新答案。
public class Main
{
static int ans = -1;
public static void find(int[] arr,int sum1,int sum2,int start)
{ if(sum1==sum2)
ans = Math.max(ans,sum1);
if(start==arr.length)
return;
find(arr,sum1+arr[start],sum2,start+1);
find(arr,sum1,sum2+arr[start],start+1);
find(arr,sum1,sum2,start+1);
}
public static void main(String[] args)
{
int[] arr = new int[]{1,2,100,101,6,100};
ans = -1;
find(arr,0,0,0);
System.out.println(ans);
}
}