给定一组正整数,其元素不需要是不同的,我需要找到不能从给定集合的任何子集中获得的最小非负和。
示例:if S = {1, 1, 3, 7}
,我们可以0
为(S' = {})
,1
为(S' = {1})
,2
为(S' = {1, 1})
, 3
为(S' = {3})
,4
为(S' = {1, 3})
,5
为(S' = {1, 1, 3})
,但我们无法获取6
。
现在我们获得一个array A
,由N
个正整数组成。它们是M
个查询,每个查询包含两个整数Li
和Ri
描述我的查询:我们需要找到不能从数组elements ={A[Li], A[Li+1], ..., A[Ri-1], A[Ri]}
获得的这个Sum。
我知道通过蛮力方法在O(2 ^ n)中完成它。但鉴于1 ≤ N, M ≤ 100,000
,这是不可能的。
他们采取任何有效的方法都是如此。
答案 0 :(得分:12)
假设我们有一个bool
数组,表示到目前为止尚未找到的数字(通过求和)。
对于我们在n
的有序(增加值)子集中遇到的每个数字S
,我们执行以下操作:
True
中位置i
的每个现有numbers
值,我们将numbers[i + n]
设置为True
numbers[n]
设置为True
使用这种筛子,我们会将所有找到的数字标记为True
,并在算法结束时迭代数组会找到最小的无法获得的总和。
显然,我们不能有这样的解决方案,因为数组必须是无限的才能适用于所有数字集。
通过做一些观察可以改进这个概念。输入1, 1, 3
时,数组变为(按顺序):
(数字代表true
个值)
可以做一个重要的观察:
对于7
的下一个输入,我们可以声明:
7
7
的数字,则无法获得6
我们可以得出结论:
由于(3)和(6),我们实际上不需要numbers
数组,我们只需要一个值,max
到表示到目前为止找到的最大数量强>
这样,如果下一个号码n
大于max + 1
,则会产生差距,max + 1
是最低无法获得的号码。
否则,max
变为max + n
。如果我们浏览了整个S
,则结果为max + 1
。
实际代码(C#,很容易转换为C):
static int Calculate(int[] S)
{
int max = 0;
for (int i = 0; i < S.Length; i++)
{
if (S[i] <= max + 1)
max = max + S[i];
else
return max + 1;
}
return max + 1;
}
应该运行得非常快,因为它显然是线性时间(O(n))。由于应该对函数的输入进行排序,因此使用quicksort将变为O(nlogn)。我设法在不到5分钟的时间内在8个内核上获得结果M = N = 100000
。
如果数字上限为10 ^ 9,则可以使用基数排序来近似排序的O(n)时间,但由于需要大量的排序,这仍然会超过2秒。
但是 ,我们可以使用统计概率1为randomed 来消除排序前的子集。在开始时,检查S
中是否存在1,如果不存在,则每个查询的结果为1,因为无法获取。
从统计数据来看,如果我们从10 ^ 9个数字中随机抽取10 ^ 5次,我们有99.9%的机会没有得到单个1。
在每次排序之前,检查该子集是否包含1,如果不是,则其结果为1。
通过此修改,代码在我的机器上运行2毫秒。这是http://pastebin.com/rF6VddTx
上的代码答案 1 :(得分:1)
这是subset-sum problem 的变体,NP-Complete,但您可以在此处采用伪多项式Dynamic Programming解决方案,基于此递归公式:
f(S,i) = f(S-arr[i],i-1) OR f(S,i-1)
f(-n,i) = false
f(_,-n) = false
f(0,i) = true
递归公式基本上是一个详尽的搜索,如果您可以使用元素i
或没有元素i
来获取它,则可以实现每个总和。
动态编程是通过构建SUM+1 x n+1
表来实现的(其中SUM
是所有元素的总和,n
是元素的数量),并自下而上构建它
类似的东西:
table <- SUM+1 x n+1 table
//init:
for each i from 0 to SUM+1:
table[0][i] = true
for each j from 1 to n:
table[j][0] = false
//fill the table:
for each i from 1 to SUM+1:
for each j from 1 to n+1:
if i < arr[j]:
table[i][j] = table[i][j-1]
else:
table[i][j] = table[i-arr[j]][j-1] OR table[i][j-1]
获得该表后,您需要最小的i
,以便所有j
:table[i][j] = false
解决方案的复杂性为O(n*SUM)
,其中SUM
是所有元素的总和,但请注意,在找到所需数字后,实际上可以修剪算法,而无需继续下一行,这是解决方案所不需要的。