你如何从给定的N个数字中测试所有可能的加法组合,使它们加起来得到给定的最终数字?
一个简单的例子:
要添加的数字集:N ={1,5,22,15,0,…} 期望结果:12345
你如何从给定的N个数字中测试所有可能的加法组合,使它们加起来得到给定的最终数字?
一个简单的例子:
要添加的数字集:N ={1,5,22,15,0,…} 期望结果:12345
当前回答
PHP版本,灵感来自Keith Beller的c#版本。
bala的PHP版本不适合我,因为我不需要对数字进行分组。我想要一个更简单的实现,只有一个目标值和一个数字池。这个函数也会删除任何重复的条目。
编辑25/10/2021:添加精度参数以支持浮点数(现在需要bcmath扩展)。
/**
* Calculates a subset sum: finds out which combinations of numbers
* from the numbers array can be added together to come to the target
* number.
*
* Returns an indexed array with arrays of number combinations.
*
* Example:
*
* <pre>
* $matches = subset_sum(array(5,10,7,3,20), 25);
* </pre>
*
* Returns:
*
* <pre>
* Array
* (
* [0] => Array
* (
* [0] => 3
* [1] => 5
* [2] => 7
* [3] => 10
* )
* [1] => Array
* (
* [0] => 5
* [1] => 20
* )
* )
* </pre>
*
* @param number[] $numbers
* @param number $target
* @param array $part
* @param int $precision
* @return array[number[]]
*/
function subset_sum($numbers, $target, $precision=0, $part=null)
{
// we assume that an empty $part variable means this
// is the top level call.
$toplevel = false;
if($part === null) {
$toplevel = true;
$part = array();
}
$s = 0;
foreach($part as $x)
{
$s = $s + $x;
}
// we have found a match!
if(bccomp((string) $s, (string) $target, $precision) === 0)
{
sort($part); // ensure the numbers are always sorted
return array(implode('|', $part));
}
// gone too far, break off
if($s >= $target)
{
return null;
}
$matches = array();
$totalNumbers = count($numbers);
for($i=0; $i < $totalNumbers; $i++)
{
$remaining = array();
$n = $numbers[$i];
for($j = $i+1; $j < $totalNumbers; $j++)
{
$remaining[] = $numbers[$j];
}
$part_rec = $part;
$part_rec[] = $n;
$result = subset_sum($remaining, $target, $precision, $part_rec);
if($result)
{
$matches = array_merge($matches, $result);
}
}
if(!$toplevel)
{
return $matches;
}
// this is the top level function call: we have to
// prepare the final result value by stripping any
// duplicate results.
$matches = array_unique($matches);
$result = array();
foreach($matches as $entry)
{
$result[] = explode('|', $entry);
}
return $result;
}
例子:
$result = subset_sum(array(5, 10, 7, 3, 20), 25);
这将返回一个包含两个数字组合数组的索引数组:
3, 5, 7, 10
5, 20
浮点数示例:
// Specify the precision in the third argument
$result = subset_sum(array(0.40, 0.03, 0.05), 0.45, 2);
这将返回一个匹配项:
0.40, 0.05
其他回答
下面是一个更好的版本,具有更好的输出格式和c++ 11特性:
void subset_sum_rec(std::vector<int> & nums, const int & target, std::vector<int> & partialNums)
{
int currentSum = std::accumulate(partialNums.begin(), partialNums.end(), 0);
if (currentSum > target)
return;
if (currentSum == target)
{
std::cout << "sum([";
for (auto it = partialNums.begin(); it != std::prev(partialNums.end()); ++it)
cout << *it << ",";
cout << *std::prev(partialNums.end());
std::cout << "])=" << target << std::endl;
}
for (auto it = nums.begin(); it != nums.end(); ++it)
{
std::vector<int> remaining;
for (auto it2 = std::next(it); it2 != nums.end(); ++it2)
remaining.push_back(*it2);
std::vector<int> partial = partialNums;
partial.push_back(*it);
subset_sum_rec(remaining, target, partial);
}
}
建议回答:
下面是一个使用es2015生成器的解决方案:
function* subsetSum(numbers, target, partial = [], partialSum = 0) {
if(partialSum === target) yield partial
if(partialSum >= target) return
for(let i = 0; i < numbers.length; i++){
const remaining = numbers.slice(i + 1)
, n = numbers[i]
yield* subsetSum(remaining, target, [...partial, n], partialSum + n)
}
}
使用生成器实际上非常有用,因为它允许您在找到有效子集时立即暂停脚本执行。这与没有生成器(即缺乏状态)的解决方案形成对比,后者必须遍历每个数字子集
c#版本的@msalvadores代码的答案
void Main()
{
int[] numbers = {3,9,8,4,5,7,10};
int target = 15;
sum_up(new List<int>(numbers.ToList()),target);
}
static void sum_up_recursive(List<int> numbers, int target, List<int> part)
{
int s = 0;
foreach (int x in part)
{
s += x;
}
if (s == target)
{
Console.WriteLine("sum(" + string.Join(",", part.Select(n => n.ToString()).ToArray()) + ")=" + target);
}
if (s >= target)
{
return;
}
for (int i = 0;i < numbers.Count;i++)
{
var remaining = new List<int>();
int n = numbers[i];
for (int j = i + 1; j < numbers.Count;j++)
{
remaining.Add(numbers[j]);
}
var part_rec = new List<int>(part);
part_rec.Add(n);
sum_up_recursive(remaining,target,part_rec);
}
}
static void sum_up(List<int> numbers, int target)
{
sum_up_recursive(numbers,target,new List<int>());
}
function solve(n){
let DP = [];
DP[0] = DP[1] = DP[2] = 1;
DP[3] = 2;
for (let i = 4; i <= n; i++) {
DP[i] = DP[i-1] + DP[i-3] + DP[i-4];
}
return DP[n]
}
console.log(solve(5))
这是JS的一个动态解决方案,告诉任何人有多少种方法可以得到一定的总和。如果考虑到时间和空间的复杂性,这可能是正确的解决方案。
这个问题的一个迭代c++堆栈解决方案。与其他迭代解决方案不同的是,它不会对中间序列进行不必要的复制。
#include <vector>
#include <iostream>
// Given a positive integer, return all possible combinations of
// positive integers that sum up to it.
std::vector<std::vector<int>> print_all_sum(int target){
std::vector<std::vector<int>> output;
std::vector<int> stack;
int curr_min = 1;
int sum = 0;
while (curr_min < target) {
sum += curr_min;
if (sum >= target) {
if (sum == target) {
output.push_back(stack); // make a copy
output.back().push_back(curr_min);
}
sum -= curr_min + stack.back();
curr_min = stack.back() + 1;
stack.pop_back();
} else {
stack.push_back(curr_min);
}
}
return output;
}
int main()
{
auto vvi = print_all_sum(6);
for (auto const& v: vvi) {
for(auto const& i: v) {
std::cout << i;
}
std::cout << "\n";
}
return 0;
}
输出print_all_sum (6):
111111
11112
1113
1122
114
123
15
222
24
33