找到最小可能的数字,该数字不能表示为序列中1,2或其他数字的总和

时间:2014-03-18 14:44:51

标签: c++ algorithm

我是C ++的新手,需要在以下任务中提供逻辑帮助。

给定n个正整数的序列(n <10 ^ 6;每个给定的整数小于10 ^ 6),编写程序以找到最小的正整数,其不能表示为1,2的总和或者给定序列的更多项目(即每个项目可以被取0或1次)。示例:输入:2 3 4,输出:1;输入:1 2 6,输出:4

我似乎无法构造它的逻辑,为什么最后的输出是4以及如何在C ++中实现它,非常感谢任何帮助。 到目前为止,这是我的代码:

    #include<iostream>
    using namespace std;

    const int SIZE = 3;

    int main()
    {
//Lowest integer by default
int IntLowest = 1;
int x = 0;
//Our sequence numbers
int seq;
int sum = 0;
int buffer[SIZE];
//Loop through array inputting sequence numbers
for (int i = 0; i < SIZE; i++)
{
    cout << "Input sequence number: ";
    cin >> seq;
    buffer[i] = seq;
    sum += buffer[i];
}
int UpperBound = sum + 1;

int a = buffer[x] + buffer[x + 1];
int b = buffer[x] + buffer[x + 2];
int c = buffer[x + 1] + buffer[x + 2];
int d = buffer[x] + buffer[x + 1] + buffer[x + 2];


for (int y = IntLowest - 1; y < UpperBound; y++)
{
    //How should I proceed from here?

}
return 0;
    }

4 个答案:

答案 0 :(得分:4)

Voreno建议的答案实际上是解决0-1背包问题(http://en.wikipedia.org/wiki/Knapsack_problem#0.2F1_Knapsack_Problem)。如果您按照链接,您可以阅读如何在不构建初始集的所有子集的情况下完成它(它们太多,2 ^ n)。如果约束条件有点小,就像10 ^ 3那样它会起作用。

但是当n = 10 ^ 6时,它仍然需要太多的时间和空间。但是没有必要解决背包问题 - 我们只需要找到我们无法获得的第一个数字。

更好的解决方案是对数字进行排序,然后迭代一次,找到数组的每个前缀数x,这样使用该前缀就可以得到区间[1..x]中的所有数字。此时我们无法获得的最小数字是x + 1.当您考虑下一个数字a [i]时,您有两个选择:

  1. a [i]&lt; = x + 1,然后你可以得到所有数字到x + a [i],
  2. a [i]&gt; x + 1,那么你就得不到x + 1了,你就得到了答案。
  3. 示例:

    您将获得数字1,4,12,2,3。

    你对它们进行排序(得到1,2,3,4,12),从x = 0开始,考虑每个元素并按以下方式更新x:

    1&lt; = x + 1,因此x = 0 + 1 = 1。

    2&lt; = x + 1,因此x = 1 + 2 = 3。

    3 <= x + 1,因此x = 3 + 3 = 6.

    4&lt; = x + 1,因此x = 6 + 4 = 10.

    12&gt; x + 1,所以我们找到了答案,它是x + 1 = 11。

    (编辑:修正了一个错误,添加了示例。)

答案 1 :(得分:3)

我认为这可以在O(n)时间和O(log2(n))存储器复杂性中完成。 假设使用O(1)中的BSR(最高设置位索引)(floor(log2(x)))实现。

算法:

1创建一个(log2(MAXINT))桶的数组,在10 ^ 6的情况下为20,每个桶包含sum和min值(init:min = 2 ^(i + 1)-1,sum = 0 )。 (懒惰的初始化可以用于小n)

2对输入进行一次传递,将每个值存储在桶[bsr(x)]中。

for (x : buffer) // iterate input
    buckets[bsr(x)].min = min(buckets[bsr(x)].min, x)
    buckets[bsr(x)].sum += x

3迭代桶,维持无法访问:

int unreachable = 1 // 0 is always reachable
for(b : buckets)
    if (unreachable >= b.min)
        unreachable += b.sum 
    else
        break
return unreachable

这是有效的,因为假设我们在桶i,我们考虑两种情况:

不可达&gt; = b.min为真:因为该桶包含范围[2 ^ i ... 2 ^(i + 1)-1]中的值,这意味着2 ^ i <= b。分钟。反过来,b.min&lt; =无法到达。因此无法到达+ b.min&gt; = 2 ^(i + 1)。这意味着可以添加桶中的所有值(在添加b.min之后,所有其他值都更小),即无法访问+ = b.sum。

unreachable&gt; = b.min为false:这意味着b.min(剩余序列中的最小数字)大于不可达。因此我们需要返回无法访问。

答案 2 :(得分:0)

第二个输入的输出为4,因为如果您只能将每个项目仅取0或1次,则这是最小的正数,不能表示为1,2或6的总和。我希望这可以帮助你理解更多:

该列表中有3个项目:1,2,6 从最小的正整数开始,您开始检查该整数是否可以是给定序列的1个或更多个数之和的结果。

1 = 1+0+0
2 = 0+2+0
3 = 1+2+0
4 cannot be expressed as a result of the sum of one of the items in the list (1,2,6). Thus 4 is the smallest positive integer which cannot be expressed as a sum of the items of that given sequence.

答案 3 :(得分:-1)

最后一个输出是4,因为:

1 = 1
2 = 2
1 + 2 = 3
1 + 6 = 7
2 + 6 = 8
1 + 2 + 6 = 9

因此,输入(1,2,6)的任何组合都无法表示的最小整数为4.

问题是: 第1部分。找到可以由输入数字表示的最大可能整数(即,给出的所有数字的总和),它给出了上限

UpperBound = sum(all_your_inputs) + 1

第2部分。通过组合您给出的不同整数,找到您可以获得的所有整数。即如果给你a,b和c作为整数,找到:

a + b,a + c,b + c和a + b + c

第2部分)+整数列表,为您提供使用数字可以获得的所有整数。

  1. 从1到UpperBound的每个整数循环

    表示i = 1到UpperBound        如果我不是=从第2点开始的列表中的数字)            i =你的最小整数            破

  2. 这是一种笨拙的做法,但我确信通过一些数学可以找到更好的方法吗?

    编辑:改进的解决方案

    //sort your input numbers from smallest to largest
    input_numbers = sort(input_numbers)
    //create a list of integers that have been tried numbers
    tried_ints = //empty list
    for each input in input_numbers
           //build combinations of sums of this input and any of the previous inputs
           //add the combinations to tried_ints, if not tried before
           for 1 to input
               //check whether there is a gap in tried_ints
               if there_is_gap
                    //stop the program, return the smallest integer
                    //the first gap number is the smallest integer