数组算法的组合
Combinations of an array algorithm
我想找到一个大小为5、加起来为15的数组的组合。做这件事最好的方法是什么。
假设我有阵列
7 8 10 5 3
在C++中查找所有加起来为15的数字的最佳方法是什么
如果,正如您在评论中提到的,10是问题中的最高数字(也是元素的最大数量)。然后使用蛮力(使用巧妙的比特掩码,请参阅本教程):
// N is the number of elements and arr is the array.
for (int i = 0; i < (1 << N); ++i) {
int sum = 0;
for (int j = 0; j < N; ++j) if (i & (1 << j)) sum += arr[j];
if (sum == required_sum); // Do something with the subset represented by i.
}
该算法的复杂度为O(N*2^N)。注意,只要N<32.注意,具有一定和的子集的数量可以是指数的(大于2^(N/2))。例如,{1,1,1…,1}和sum=N/2。
然而,如果N很大,但N*required_sum不是很大(高达数百万),则可以使用以下递归(具有动态编程或记忆):
f(0, 0) = 1
f(0, n) = 0 where n > 0
f(k, n) = 0 where k < 0
f(k + 1, S) = f(k, S - arr[k]) + f(k, S) where k >= 0
其中f(k,S)表示获得具有元素0..k的子集的和S的可能性。动态编程表可用于生成所有子集。生成表格的运行时间为O(N*S),其中S是所需的总和。从表生成子集的运行时间与此类子集的数量成比例(可能非常大)。
关于该问题的一般说明:
这个问题一般是NP完全问题。因此,它没有已知的多项式时间算法。然而它确实有一个伪多项式时间算法,即上面的递归。
"最佳"方式取决于您正在优化的内容。
如果数组中元素不多,有一个简单的组合算法:对于从1到n
的所有长度(其中n
是数组中元素的数量),检查所有可能的n
数字集,并打印每个数字的总和为15。
从实施时间的角度来看,这可能是最好的。从运行时效率的角度来看,动态编程解决方案(这是一个DP问题)可能是最好的;这里的DP解是O(N³)
,其中的组合解远不止于此。
DP算法的要点(我不是在写代码)是遍历你的数组,并跟踪到目前为止你看到的子数组中所有可能的和。当你到达每个新的数组元素时,遍历之前得到的所有部分和,并将其相加(而不是删除原始的部分和)。每当某个东西达到15或超过它时,从你正在跟踪的集合中丢弃这个和(如果它正好达到15,就打印出来)。
我的建议是使用递归。
跟踪baseindex和currentindex并尝试在每次递归中累积值
当累计值为15时,返回currentindex的整数值否则,如果currentindex达到5并且累计值不是15,则返回0
当return为0并且baseindex仍然小于5时,将1添加到baseindex并重置当前索引和累积值,然后再次开始递归。
对元素数组进行排序。维护两个指针,一个在排序数组的开头,另一个在数组的末尾。如果两个元素之和大于15,则减小第二个指针。如果总和小于15,则增加第一个指针。如果sum等于15,则记录这两个元素,并增加第一个指针。
希望它能起作用。
void getCombinations( std::vector<int>& _list, std::vector<std::vector<int>>& _output,
std::vector<int>& _cSumList = std::vector<int>(), int _sum = 0 )
{
for ( std::vector<int>::iterator _it = _list.begin(); _it < _list.end(); ++_it)
{
_sum += *_it;
_cSumList.push_back( *_it );
std::vector<int> _newList;
for ( std::vector<int>::iterator _itn = _list.begin(); _itn < _list.end(); ++_itn )
if ( *_itn != *_it )
_newList.push_back( *_itn );
if ( _sum < 15 )
getCombinations( _newList, _output, _cSumList, _sum );
else if ( _sum == 15 )
{
bool _t = false;
for ( std::vector<std::vector<int>>::iterator _itCOutputList = _output.begin(); _itCOutputList < _output.end(); ++_itCOutputList )
{
unsigned _count = 0;
for ( std::vector<int>::iterator _ita = _itCOutputList->begin(); _ita < _itCOutputList->end(); ++_ita )
for ( std::vector<int>::iterator _itb = _cSumList.begin(); _itb < _cSumList.end(); ++_itb )
if ( *_itb == *_ita )
++_count;
if ( _count == _cSumList.size() )
_t = true;
}
if ( _t == false )
_output.push_back( _cSumList );
}
_cSumList.pop_back();
_sum -= *_it;
}
}
您的号码示例用法:
int _tmain(int argc, _TCHAR* argv[])
{
std::vector<int> list;
list.push_back( 7 );
list.push_back( 8 );
list.push_back( 10 );
list.push_back( 5 );
list.push_back( 3 );
std::vector<std::vector<int>> output;
getCombinations( list, output );
for ( std::vector<std::vector<int>>::iterator _it = output.begin(); _it < output.end(); ++_it)
{
for ( std::vector<int>::iterator _it2 = (*_it).begin(); _it2 < (*_it).end(); ++_it2)
std::cout << *(_it2) << ",";
std::cout << "n";
}
std::cin.get();
return 0;
}
最佳方式是主观的。正如我所说,上面的代码可以得到极大的改进,但应该为您提供一个起点。
- 构建可组合有向图(扫描仪生成器的汤普森构造算法)
- 计算数组重复次数的组合的有效算法,加起来达到给定的总和
- 为 C++11 算法组合多个谓词
- 双重释放或损坏(输出):使用向量的组合算法0x0000000001a880a0***
- Python到C++:使用递归列出背包的所有组合的算法
- 从给定的 IPv6:端口列表中搜索 IPv6:端口组合的最快搜索算法是什么 O(1) 时间一致性
- 给定数字与重复的组合的算法?C
- 排列和组合生成算法
- 加密++对称算法和经过身份验证的块模式组合
- 所有的组合算法和解决C++问题的一般方法
- 按字典顺序打印给定字符串的所有字母组合的算法
- 生成每个可能的 7 位数字组合的算法
- 运行整数数组所有组合的算法
- 根据组合框中的选定项目选择算法
- C++算法优化:从N个元素中求出K组合
- 递归算法将所有组合分成两组
- 数组算法的组合
- 按类型算法的列表组合
- 一个很好的算法来获得元素的闲置组合
- 是硬币变化算法,输出仍可由DP解决的所有组合