两个子集的总和尽可能接近

时间:2011-08-25 11:40:40

标签: algorithm math

  迈克在家中发现了一条长长的磁带。他接着写了一些   整数序列。现在他想在这样的地方切割磁带   一部分和整数之和的差异   另一个尽可能接近零(一方面必须如此)   至少一个数字)。你要打印这个的绝对值   差。

     

输入:n(2≤n≤10 6 )表示数字的数量   写在磁带上然后是n个整数a i (-10 3   ≤一个 1 ≤10 3 )作为写在上面的数字   胶带。

     

输出:一个整数是差值的最小绝对值   两部分之间。

     

示例:
6
1 2 3 4 5 6
输出:
1

我有一种感觉,我之前曾在某处读过这样的问题..但我不知道如何解决它。我的意思是,我有一个线索,但我不知道它是否正确。我应该先计算整个磁带的总和,然后从左到右计算,直到我尽可能接近整个磁带的一半?我的意思是:我从左到右总结数字,不断检查我是否超过了整个集合的一半。如果子集的总和等于一半 - 我们打印零。如果确切的一半是不可能的,我们检查最接近的下方和上方并输出最接近的一个。那可以吗?

2 个答案:

答案 0 :(得分:0)

从磁带的一端开始计算总和。记下你正在进行的子总和(总和那个数字)。现在问题是一个简单的搜索问题,我们可以使用例如binary search搜索最接近总和除以2的子和。

答案 1 :(得分:0)

是的,这正是您要做的。您可以通过先对数组求和,然后从右总和中减去每个元素并将其添加到左总和来跟踪左分区和右分区的总和。

auto right_sum = accumulate(tape.begin(),tape.end(),0); // sum all the elements
decltype(right_sum) left_sum = 0;
decltype(right_sum) smallest_difference = abs(right_sum-left_sum);
auto best_partition = tape.begin();
for (auto it=tape.begin();it!=tape.end();++it) {
  left_sum += *it;
  right_sum -= *it;
  if (abs(right_sum-left_sum)<smallest_difference) {
    smallest_difference = abs(right_sum-left_sum);
    best_partition = it;
  } 
}