当前位置: 首页 > 知识库问答 >
问题:

无法从集合中获得的最小和

宗政兴发
2023-03-14

给定一组正整数,其元素不必不同,我需要找到从给定集合的任何子集都不能得到的最小非负和。

例如:如果S={1,1,3,7},我们可以得到0作为(S={})1作为(S={1})2作为(S={1,1})34作为(S“={1,3})5作为(S“={1,1,3}),但我们无法得到6

现在我们得到一个数组A,由N个正整数组成。它们是M个查询,每个查询由两个整数组成,分别描述i't查询:我们需要找到无法从数组元素{A[Li],A[Li 1],…,A[Ri-1],A[Ri]}中获得的和。

我知道用蛮力方法找到它要在O(2^n)中完成。但是给定1≤N,M≤100,000。这是做不到的。他们的任何有效方法也是如此。

共有2个答案

唐利
2023-03-14

这是子集和问题的一种变体,它是NP完全的,但这里可以采用基于递归公式的伪多项式动态规划解决方案:

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,这样对于所有jtable[i][j]=false

解决方案的复杂性为O(n*SUM),其中SUM是所有元素的总和,但请注意,在找到所需的数字后,实际上可以修剪算法,而无需继续下一行,这是解决方案不需要的。

公冶高峯
2023-03-14

假设我们有一个bool数组,表示迄今为止尚未找到的数字(通过求和的方式)。

对于我们在S的有序(递增值)子集中遇到的每个数字,我们执行以下操作:

  1. 对于编号中位置i处的每个现有值,我们将编号设置为真

使用这种筛选,我们会将所有找到的数字标记为True,当算法完成时,遍历数组会找到最小的不可求和。

显然,我们不能有这样的解决方案,因为数组必须是无限的,才能处理所有的数集。

这个概念可以通过做一些观察来改进。输入1,1,3,数组变成(按顺序):

可以提出一个重要的意见:

  • (3) 对于下一个数字,如果已经找到之前的数字,则会将其添加到所有这些数字中。这意味着,如果在数字之前没有间隙,则在处理该数字之后将没有间隙

对于7的下一个输入,我们可以断言:

  • (4)由于输入集是有序的,因此不会有小于7
  • 的数字
  • (5)如果没有小于7的数字,则无法获得6

我们可以得出以下结论:

  • (6) 第一个缺口表示无法达到的最小数量

由于(3)和(6),我们实际上不需要数字数组,我们只需要一个值,即max来表示迄今为止找到的最大数字。

这样,如果下一个数字n大于max 1,则会产生一个间隙,而max 1是最小的无法获得的数字。

否则,max变为max n。如果我们遍历了整个代码,结果是最大值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))。由于应该对函数的输入进行排序,使用快速排序时,这将变为O(nlogn)。我在不到5分钟的时间内,在8个内核上获得了结果,M=N=100000。

如果数字上限为10^9,则可以使用基数排序来近似O(n)时间进行排序,但是由于需要大量的排序,这仍然会超过2秒。

但是,我们可以在排序之前使用随机1的统计概率来消除子集。开始时,检查S中是否存在1,如果不存在,则每个查询的结果都是1,因为无法获取。

从统计学上讲,如果我们从10^9个数字中随机抽取10^5次,我们有99.9%的几率得不到一个1。

在每次排序之前,检查该子集是否包含1,如果不包含1,则其结果为1。

通过此修改,代码在我的机器上运行只需2毫秒。这是http://pastebin.com/rF6VddTx上的代码

 类似资料:
  • 我有一个名为product的MongoDB集合,其中包含以下文档,如下所示。 我想查询收款情况,只退回价格最低的单据,例如: 但当我运行聚合查询时: 它只返回一个文档。

  • 问题内容: 我有ID为的商品。现在我有如下数据。每行都有一个offerId。由数组中的组合组成。是那个的价值 现在,我必须选择所有给我提供最佳ID组合(即最大总折扣)的offerId。 例如,在上述情况下:可能的结果可能是: [o2,o4,o5]最大折扣为。 注意。结果offerId应该不会重复ID。id的示例为[1,3,4],[5],[6]都是不同的。 其他组合可以是: 其id为[1],[3,5

  • 我正在写一个方法,它接受用户的整数输入,并显示总数、平均值、最大值和最小值。 我有总数和平均工作,但我得到2147483647最大和-2147483648最小。 循环只能在用户输入-1时结束。 我的代码:

  • 问题内容: 从对象开始时:将小时作为表现的最佳方法是什么? 我必须迭代几百万个日期,因此性能很重要。 通常情况下,我会得到以下小时,但是也许有更好的方法? 问题答案: 在UTC中: 要么

  • 无法从外壳中删除集合, 集合可用并且我的 php 脚本正在访问它的东西(选择|更新) 但当我使用: 它给了我一个错误:

  • 我正在尝试获取mongodb中存在的所有数据库的值,迭代所有数据库和集合,然后打印it文档。我可以打印作为变量传递集合的文档,但不能在所有数据库和集合上进行迭代(作为变量的值)。有人知道pymongo是否支持动态地作为值传递,而不是将集合和数据库作为变量本身传递?