如果我有n-r
个数字,从1 to n
中间缺少r
个数字,那么如何计算可以通过添加这些数字形成的所有可能数字(或者分组为2/3/4/5/6 ......)。
例如,假设我有5-2
个数字,
也就是说,1 2 4
和3 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中的第一个数字,我无法使用给定数字的组合形成。一个简单的逻辑就可以了。谢谢!
答案 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)
set
开头
set
和您的第一个号码的所有可能总和(如果您的第一个号码是1,那么您的set
包含1和0)set
包含0,1,2和3) 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)
对于只包含正整数的输入set
或multiset
,Evgeny Kluev's Solution是 O(2n),[我的其他解决方案]是 O(nlogn)
对于给定的输入number
,您可以找到不可变形的数字,如下所示:
number
的元素收集到int_log[i]
i = int(log2(
元素))
S[]
int_log[]
S[x] + 1
,其中int_log[x + 1]
不包含小于或等于S[x] + 1
的元素要证明 [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;