计算动态编程中的限制

时间:2018-06-02 04:44:33

标签: c++11 math dynamic

我在topcoder上找到了这个问题:

  

你的朋友卢卡斯给了你一个正整数序列S.

     

有一段时间,你们两个用S玩了一个简单的游戏:Lucas会选择一个数字,你必须选择S的一些元素,这样你选择的所有数字的总和就是Lucas选择的数字。例如,如果S = {2,1,2,7}并且Lucas选择了数字11,那么您将回答2 + 2 + 7 = 11。

     

卢卡斯现在想通过选择数字X来欺骗你,这样就没有有效的答案了。例如,如果S = {2,1,2,7},则无法选择总和为6的S元素。

     

给定int [] S.找到无法获得的最小正整数X作为S的某些(可能是所有)元素的总和。

     

约束: - S将包含1到20个元素,包括1和20个元素。 - S的每个元素都在1到100,000之间,包括在内。

但是在编辑解决方案中已经写了:

  

如何找到最小的不可能的总和?好吧,我们可以尝试以下天真算法:首先尝试使用x = 1,如果这不是有效总和(使用上一节中的方法找到),那么我们可以返回x,否则我们增加x并再次尝试,再次,直到我们找到不是有效总和的最小数字。

     

让我们找到迭代次数的上限,我们需要在找到结果之前尝试x的值的数量。首先,这个问题可能的最大总和是100000 * 20(所有数字都是最大值100000),这意味着100000 * 20 + 1将不是一个不可能的值。我们肯定最多需要2000001步。

     

这个上界有多好?如果我们在20个数字中的每一个中都有100000,则1不可能是总和。所以在这种情况下我们实际上需要一次迭代。如果我们想要1是一个可能的总和,我们应该在初始元素中有1。然后我们需要一个2(否则我们只需要2次迭代),然后一个4(3可以通过添加1 + 2找到),然后8(5到7的数字可以通过添加一些前3个幂来找到2),然后是16,32 ......事实证明,在2的幂下,我们可以轻松地进行需要多次迭代的输入。使用前两个的17个幂,我们可以覆盖最初的262143个整数。对于最大的数字,这应该是一个很好的估计。 (我们不能在输入中使用2 ^ 18,小于100000)。

     

最多262143次,我们需要查询数字x是否在可能的总和中。我们可以在这里使用布尔数组。看来,即使O(log(n))数据结构也应该足够快。

我确实理解了第一段。但在那之后他们已经解释了一些关于"这个上限有多好?..."。我无法理解这一段。他们是如何推断我们需要查询262143次,如果数字x在可能的总和中?

我是动态编程的新手,所以如果有人能向我解释这一点会很棒。

谢谢。

1 个答案:

答案 0 :(得分:0)

这个想法如下:

如果输入序列包含两个k的第一个2^0, 2^1, ... 2^(k-1)幂,那么总和可以是0(2^k) - 1之间的任意整数。由于可以出现在序列中的2的最大幂是2^17,因此可以从18个数字构建的最大总和是2^18 - 1=262,143。如果缺少2的幂,那么将有一个较小的总和是无法实现的。

然而,该声明缺少序列中可能还有2个数字(最多20个)。从这两个数字中,您可以重复相同的过程。因此,要检查的最大数量实际上是(2^18) - 1 + (2^2) - 1

你可能想知道为什么我们使用两种权力而不是任何其他权力。原因是我们对输入序列中的数字执行二进制选择。要么我们在总和上添加一个数字,要么我们不添加。因此,如果我们将数字ni的选择表示为选择变量si(0或1),则可能的总和为:

s = s0 * n0 + s1 * n1 + s2 * n2 + ...

现在,如果我们选择ni为两个ni = 2^i的权力,那么:

s = s0 * 2^0 + s1 * 2^1 + s2 * 2^2 + ...
  = sum si * 2^i

这相当于数字的二进制表示(参见Positional Notation)。根据定义,选择变量的不同选择将产生不同的总和。因此,通过在输入序列中选择2的幂,可能的和的数量是最大的。