通过添加给定数字形成的所有可能数字

时间:2015-01-08 17:04:44

标签: c++ algorithm

如果我有n-r个数字,从1 to n中间缺少r个数字,那么如何计算可以通过添加这些数字形成的所有可能数字(或者分组为2/3/4/5/6 ......)。

例如,假设我有5-2个数字, 也就是说,1 2 43 5丢失了。现在,我可以形成

1 - {1}
2 - {2}
3 - {1,2}
4 - {4}
5 - {1,4}
6 - {4,2}
7 - {1,2,4}
8 - Cannot be formed

这是我需要找出的,这是1中的第一个数字,我无法使用给定数字的组合形成。一个简单的逻辑就可以了。谢谢!

4 个答案:

答案 0 :(得分:0)

S[i]成为可以从您的数字的第一个i形成的数字集合。然后给出S[i],可以很容易地构建S[i+1]:从S[i]开始,然后添加s+r形式的所有数字,其中s位于{ {1}}和S[i]是您列表中的r号码。

因此,您可以迭代地构建集合(i+1)s[0] = {0}, S[1],...,S[n-r]包含所有可能的总和。

以下是您的示例:

S[n-r]

答案 1 :(得分:0)

  1. 以包含0
  2. set开头
  3. 查找set和您的第一个号码的所有可能总和(如果您的第一个号码是1,那么您的set包含1和0)
  4. 现在添加下一个号码(如果您的下一个号码为2,则set包含0,1,2和3)
  5. 所以......
  6. set包含所有可能的数字。您需要通过set进行迭代并找到第一个差距。如果您的设置中有负数,这将甚至

    我知道你说你只是想要逻辑,但如果你想在下面看到一个解决方案鼠标:

      

    set<int> foo{ 0 };
     vector<int> numbers{ 4, 1, 2 };

     for (auto& i : numbers){
     set<int> temp;

     for_each(foo.begin(), foo.end(),[&](int j){temp.insert(i + j);});
     foo.insert(temp.begin(), temp.end());
     }

     for (auto& i : foo){
     cout << i << endl;
     }
     cout << "First number that cannot be formed " << *mismatch(foo.begin(), prev(foo.end()), next(foo.begin()), [](int first, int second){return first + 1 == second;}).first + 1 << endl;

    如果您尝试使用STL算法在set中搜索差距,我发现这有点困难。问题是你需要将迭代器的增量与其比较分开。

    因此,例如,这不会起作用:

      

    <击> auto i = foo.begin();
      while (++i != prev(foo.end()) && *prev(i) + 1 == *i);
      cout << "First number that cannot be formed " << *prev(i) + 1 << endl;

    因为如果所有数字都是顺序*prev(i) + 1是该集合中的最后一个值。

    此选项可用:

    i = foo.begin();
    while (i != prev(foo.end()) && *i + 1 == *next(i)){
        ++i;
    }
    cout << "First number that cannot be formed " << *i + 1 << endl;
    

    但这假设您的set始终包含至少0。

答案 2 :(得分:0)

逻辑是为每个连续的整数构建所有可能的总和,从1开始。 通过跟踪所有可能的总和并仅检查整数对的总和,可以简化问题。伪代码(未经测试和错误)看起来像:

const std::vector<unsigned> l = {1,2,4};
const unsigned sum = std::accumulate(l.begin(), l.end());
typedef std::vector<unsigned> Sum; // one possibility for a single value
typedef std::vector<Sum> Sums; // all possibilities for a single value
// the element all[i] will provide all possible subsets where the sum is equal to 'i'
std::vector<Sums> all(sum + 2); // we know that sum + 1 is impossible
// initialize with single values
for (auto i: l)
{
    all[i].push_back(Vector(1, i));
}
unsigned i = 1; // ignore 0
// stop as soon as a value doesn't have any subset
while (!all[i].empty())
{
    ++i;
    for (unsigned j = 1; i/2 > j; ++j)
    {
        const unsigned k = i - j;
        // as i == j+k, create all the relevant combinations
        for (const auto &sj: all[j])
        {
            for (const auto &sk: all[k])
            {
                all[i].push_back(sj);
                all[i].back.insert(all[i].end(), sk.begin(), sk.end());
            }
        }
    }
    if (0 == (i % 2))
    {
        // create all the possible decompositions out of i/2
        for (auto left = all[i/2].begin(); all[i/2].end() != left; ++left)
        {
            for (auto right = left + 1; all[i/2].end() != right; ++ right)
            {
                all[i].push_back(*left);
                all[i].back.insert(all[i].end(), right->begin(), right->end());
            }
        }
    }
}

需要解决的一个问题是:拒绝相同数字出现多次的总和。

答案 3 :(得分:0)

对于只包含正整数的输入setmultisetEvgeny Kluev's Solution O(2n),[我的其他解决方案]是 O(nlogn)

对于给定的输入number,您可以找到不可变形的数字,如下所示:

  1. number的元素收集到int_log[i] i = int(log2(元素))
  2. 查找S[]
  3. 的前缀和int_log[]
  4. 第一个不可变形的数字是S[x] + 1,其中int_log[x + 1]不包含小于或等于S[x] + 1的元素
  5. 要证明 [0,S[i]] 范围内的所有数字都可以由小于2 i-1的int_log子集构成见:https://math.stackexchange.com/questions/1099359/0-sum-representable-by-numbers-in-a-set

    set<int> numbers{ 1, 2, 5, 6, 7 };
    unordered_multimap<unsigned long, decltype(numbers)::key_type> int_log;
    
    for (auto& value : numbers){
        auto key = decltype(int_log)::key_type{};
    
        _BitScanReverse(&key, value); //If using gcc you'll need to use __builtin_clz here
        int_log.insert(make_pair(key, value));
    }
    
    auto result = decltype(numbers)::key_type{}; // Because we only ever need to look at the previous prefix sum value we'll use previousSum instead of a S[] here
    auto sum = decltype(numbers)::key_type{};
    auto i = decltype(int_log)::key_type{};
    auto smallest = decltype(numbers)::key_type{};
    
    do{
        result = sum;
        smallest = 2 << i;
    
        for (auto range = int_log.equal_range(i); range.first != range.second; ++range.first){
            const auto current = range.first->second;
    
            sum += current;
            smallest = min(smallest, current);
        }
        ++i;
    } while (result >= smallest - 1);
    cout << "First number that cannot be formed: " << result + 1 << endl;