我有一个我试图解决的编码问题。我还没有能够提出一个好的算法。
给定数字X(例如200),确定列表中的哪些数字(例如:4,5,10,10,23,67,889,150,50)在求和时将等于X.在这种情况下答案是(50,150)。到目前为止,我考虑首先对列表进行排序(从最低到最高)然后循环添加数字,直到我得到大于X的值。丢弃列表中的所有剩余数字,因为它们不需要(例如889)。现在我有生成总和200所需的数字列表。现在我需要确定哪些数字总和为200.
目前陷入困境。
非常感谢任何想法。
答案 0 :(得分:2)
这是一个背包问题。请参阅此处https://en.m.wikipedia.org/wiki/Knapsack_problem
答案 1 :(得分:0)
唯一可以丢弃的数字是大于目标数的数字,因为任何其他较小数字的组合可能总和达到所需数量。
要查找哪些(如果有),您必须检查剩余数字的所有可能组合。
最简单的方法是递归遍历列表,一旦添加当前数字,一旦将其删除,如果总和已经超过目标值,则修剪当前分支。
您的问题实例的示例(perl)实现在下面的代码段中给出:
my @numbers = (4, 5, 10, 10, 23, 67, 889, 150, 50);
my $length = scalar @numbers; # length of the list
sub visit {
my ($i, $sum, $goal, $items) = @_;
# i: current index, sum: sum of numbers currently selected
# goal: goal value to reach, items: list of numbers currently selected
# we found a solution !
print join (", ", @$items) . "\n" if $sum == $goal;
return if $i >= $length || $sum >= $goal;
# continue without adding
visit ($i+1, $sum, $goal, $items);
# continue with adding
visit ($i+1, $sum + $numbers[$i], $goal, [$numbers[$i], @$items]);
}
visit (0, 0, 200, []);
它会按预期报告50, 150
对。
请注意,没有尝试删除重复项,因此,除了visit (0, 0, 77, []);
三项50, 23, 4
之外,目标值为77 67, 10
的调用将被列出两次,因为有两个那里10
的实例。
哦,不要相信其他帖子中提出的任何贪婪算法,因为它们注定无法解决背包问题。
答案 2 :(得分:0)
取决于。如果你想要它快,你可以使用贪婪(ish)算法:(伪代码)
n = answer; // what you want the numbers to add up to be.
numbers = [1, 2, 40, 39,....]; //the candidates.
addList = Array[][];
addList[0] = numbers;
level = 0;
while(true){
for (number in numbers){
currnum = addlist[level][i] + number;
if(currnum == n){
return true; // or what ever you want to return
} else if (currnum < n){
addlist[level+1].append(currnum);
}
}
if (addlist[level+1].length ==0){
return false; // it will never add up to the value n.
}
level++;
}
这是严格的伪代码,我只是为了便于阅读而混合了arraylist和数组。