由给定数字相加形成的所有可能的数字
All possible numbers formed from addition of given digits
如果我有n-r
个数字,从中间缺少r
数字的1 to n
,那么我如何计算这些数字相加可以形成的所有可能的数字(以 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 中的第一个数字。一个简单的逻辑就可以了。谢谢!
逻辑是为每个连续整数构建所有可能的总和,从 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());
}
}
}
}
需要解决的错误之一:拒绝相同数字多次出现的总和。
S[i]
是一组数字,可以从数字的前i
形成。然后给定S[i]
,很容易构造S[i+1]
:从S[i]
开始,然后将形式s+r
的所有数字相加,其中s
在S[i]
中,r
是列表中(i+1)
-th数字。
因此,你可以迭代地构建集合s[0] = {0}, S[1],...,S[n-r]
,S[n-r]
包含所有可能的和。
以下是您的示例:
S[0] = {0}
r = 1: S[1] = {0} union {0+1} = {0,1}
r = 2: S[2] = {0,1} union {0+2,1+2} = {0,1,2,3}
r = 4: S[3] = {0,1,2,3} union {0+4,1+4,2+4,3+4} = {0,1,2,3,4,5,6,7}
- 从包含 0 的
set
开始 - 找到
set
和第一个数字的所有可能总和(因此,如果您的第一个数字是 1,那么您set
包含 1 和 0) - 现在添加下一个数字(所以如果你的下一个数字是 2,你的
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。
对于只包含正整数的输入set
s 或 multiset
s,Evgeny Kluev 的解是 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]
] 中的所有数字都可以由小于 2i-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;
- 有可能在Armadillo中复制MATLAB circshift方法吗
- 在他自己的方法中,有可能将一个对象取消引用到另一个对象吗
- 有可能使shared_ptr协变吗
- 有可能在信号处理程序中设置promise吗
- 是否有可能实现O(N)时间和O(1)空间解决方案,以实现C++中的字符串循环移位
- 是否有可能构建面向Linux和Windows的.Net Core C++ / CLI应用程序?
- 是否有可能使用debug_info获取ELF文件的源代码?
- C++,是否有可能/如何定义在.h和.cpp源文件中调用函数的类构造函数
- 有可能在C++中有类的查找表吗
- 是否有可能让 c++ dll 在后台运行 python 程序并让它填充向量图?如果是这样,如何?
- 向量的大小是否有可能为 1 但其中的元素数量为零?
- 是否有可能编写新的叮当声现代化规则?
- 是否有可能通过指向另一个未关联的子对象的指针来获取指向一个子对象的指针?
- 是否有可能通过演绎指南实现整个 std::make_tuple 功能?
- 是否有可能在没有复制的情况下传递 std::vector<int> 作为参数来获得 std::vector<std::array<int, 3>>?
- 是否有可能具有放入容器的移动操作的类型?
- 是否有可能通过溢出 C 中的第一个元素来写入数组第二个元素
- 声明是否有可能逃脱其封闭的名称空间
- C++ 浮点小数位,数字末尾可能突然有"05"
- 是否有可能在编译时计算数字的阶乘,但不使用枚举?