using System;
using System.Collections.Generic;
using System.Linq;
public class Test
{
public static void Main()
{
var numbers = new[] { 3, 4, 1, 6, 5, 4, 12, 13, 7, 8 };
var target = 9;
var problem = new DynamicSubsetSum(numbers, target);
foreach (var solution in problem.Solve()) {
Console.WriteLine("Solution: {0}", string.Join(", ", solution));
}
}
}
class DynamicSubsetSum
{
private readonly int[] choices;
private readonly int targetSum;
private readonly Dictionary<int, HashSet<int>> partialSolutions = new Dictionary<int, HashSet<int>>();
public DynamicSubsetSum(int[] choices, int targetSum)
{
this.choices = choices;
this.targetSum = targetSum;
}
public IEnumerable<IEnumerable<int>> Solve()
{
this.partialSolutions.Clear();
this.SolvePartial(new List<int>(this.choices), this.targetSum);
return !this.partialSolutions.ContainsKey(this.targetSum)
? Enumerable.Empty<IEnumerable<int>>()
: this.ExpandSolutions(this.choices, this.targetSum)
.Select(c => c.OrderBy(n => n))
.Distinct(new SequenceEquivalenceComparer<int>());
}
private bool SolvePartial(IEnumerable<int> partialSet, int target)
{
if (target == 0) {
return true;
}
foreach (var n in partialSet.Where(i => i <= target)) {
if (this.SolvePartial(partialSet.ExceptFirst(n), target - n)) {
this.MarkSolutionFound(target, n);
}
}
return this.partialSolutions.ContainsKey(target);
}
private void MarkSolutionFound(int target, int selection)
{
if (!this.partialSolutions.ContainsKey(target)) {
this.partialSolutions[target] = new HashSet<int>();
}
this.partialSolutions[target].Add(selection);
}
private IEnumerable<IEnumerable<int>> ExpandSolutions(IEnumerable<int> partialSet, int number)
{
if (number == 0) {
return Enumerable.Repeat(Enumerable.Empty<int>(), 1);
}
else {
return this.partialSolutions[number]
.IntersectionWith(partialSet)
.SelectMany(i => this.ExpandSolutions(partialSet.ExceptFirst(i), number - i).Select(s => s.Concat(new[] { i })));
}
}
}
class SequenceEquivalenceComparer<T> : EqualityComparer<IEnumerable<T>>
{
public override bool Equals(IEnumerable<T> lhs, IEnumerable<T> rhs)
{
return lhs.OrderBy(x => x).SequenceEqual(rhs.OrderBy(y => y));
}
public override int GetHashCode(IEnumerable<T> collection)
{
return 0;
}
}
static class HelperExtensions
{
public static HashSet<T> IntersectionWith<T>(this HashSet<T> set, IEnumerable<T> collection)
{
var copy = new HashSet<T>(set);
copy.IntersectWith(collection);
return copy;
}
public static IEnumerable<T> ExceptFirst<T>(this IEnumerable<T> collection, T itemToRemove)
{
bool found = false;
foreach (var item in collection) {
if (!found && item.Equals(itemToRemove)) {
found = true;
continue;
}
yield return item;
}
}
}