我的目标是找到总和给定总和的所有可能组合。 例如,如果数组是 2 59 3 43 5 9 8 62 10 4如果总数为12,那么可能的组合
2 10
3 9
8 4
5 3 4
这是我写的第一组代码。想知道可以做到的最佳改进。
int find_numbers_matching_sum(int *number_coll, int total)
{
int *search_till = lower_bound(number_coll,number_coll+TOTAL_SIZE, total);
int location = search_till - number_coll;
if (*search_till > total && location > 0 )
{
--location;
}
while ( location >= 0 )
{
find_totals(number_coll,total,location);
--location;
}
return 1;
}
int find_totals(int *number_coll, int total, int end_location)
{
int left_ones = total - number_coll[end_location];
int curloc = end_location;
int *search_till = 0;
int location ;
int all_numbers[10];
int i = 0;
all_numbers[i] = number_coll[end_location];
while ( left_ones && curloc >= 0 )
{
search_till = lower_bound(number_coll,number_coll+end_location, left_ones);
location = search_till - number_coll;
if (*search_till > left_ones && location > 0 )
{
--location;
}
else if ( left_ones < *search_till )
{
break;
}
curloc=location;
left_ones = left_ones - number_coll[curloc];
all_numbers[++i] = number_coll[curloc];
end_location = curloc - 1;
}
if ( !left_ones )
{
while ( i>=0)
{
cout << all_numbers[i--] << ' ';
}
}
cout << endl;
return 1;
}
答案 0 :(得分:6)
您描述的问题也称为Subset Sum Problem NP-complete。你可以实现的最好的是一个指数时间算法,它会尝试你的数组/集的所有可能的子集。
答案 1 :(得分:5)
这是子集和问题(NP-Complete),直到P?= NP,才有指数解。
来自xkcd
答案 2 :(得分:3)
这是NP-complete Knapsack Problem,subset sum problem的变体。通过连续降低N,可以线性地减少完整的背包问题。如果P!= NP成立,你将无法找到在N中以指数方式运行得快的问题的精确算法。
然而,已知多项式时间近似值。
答案 3 :(得分:3)
如果值不大,说你的总和受M限制,你可以使用dynamic programming。假设有N个项目。
想象一下你有一个矩阵DP[M][N]
。单元格DP[m][n]
表示:前n个元素中有多少组合恰好是m?
分析每个项目,您可能会也可能不会将其包含在某个组合中。然后你得到重复(处理越界值)
DP[m][n] = DP[m][n-1] + DP[m - v[n]][n - 1]
rhs的第一项意味着您正在考虑所有不使用第n项的和,第二项所有使用第n项的总和。从基础DP[0][0] = 1
开始,因为空集是有效组合,其总和为0.所需的值在DP [M] [N]中。
这是伪多项式,O(MN)
。
答案 4 :(得分:2)
#include <iostream>
#include <vector>
using namespace std;
struct State {
int v;
const State *rest;
void dump() const {
if(rest) {
cout << ' ' << v;
rest->dump();
} else {
cout << endl;
}
}
State() : v(0), rest(0) {}
State(int _v, const State &_rest) : v(_v), rest(&_rest) {}
};
void ss(int *ip, int *end, int target, const State &state) {
if(target < 0) return; // assuming we don't allow any negatives
if(ip==end && target==0) {
state.dump();
return;
}
if(ip==end)
return;
{ // without the first one
ss(ip+1, end, target, state);
}
{ // with the first one
int first = *ip;
ss(ip+1, end, target-first, State(first, state));
}
}
int main() {
int a[] = { 2,59,3,43,5,9,8,62,10,4 };
int * start = &a[0];
int * end = start + sizeof(a) / sizeof(a[0]);
ss(start, end, 12, State());
}
答案 5 :(得分:1)
这与数论中的Partition有关,可以使用动态编程来解决。
让n
为总数。让parts
成为元素列表。我们假设它们是正整数。
if parts == []
then f(n,parts) = []
else let parts = x::queue and f(n,parts) = union(L1, L2)
where:
L1 = f(n, queue)
if n-x>0
then let result = f(n-x, queue) and L2 = concatenation([x], result)
else if n-x==0, L2 = [x]
else L2 = []
这是typical作业。