Find elements in a list that, together add up to a target number

前端 未结 4 613
没有蜡笔的小新
没有蜡笔的小新 2021-01-07 11:03

Suppose I have a list:

List _arr = new List {1, 3, 4};

And a target of 4

I want to return

相关标签:
4条回答
  • 2021-01-07 11:06

    If you are after a really good and fast solution to this problem you are not alone.

    This is a well know SUBSET SUM problem. I suggest you could look it up at Wikipedia here:

    http://en.wikipedia.org/wiki/Subset_sum_problem

    Of course you could iterate through all possible subsets looking for your sum but be aware that there is (2 ^ CountOfListElements) possible combinations. If the list is always small in size you will have no problems coding it. Even exponential time solution will work for small sets.

    0 讨论(0)
  • 2021-01-07 11:07

    I think the best solution to this problem, is Dynamic Programming.

    Create a 2-Dimensional Array, with dimensions [a + 1, _arr.Length]:

      0 1 2 3 4
    1
    3
    4
    

    Then, for each column in that 2D array, fill in each cell, such that the sum of all cells of a column equals to the index of the column:

      0 1 2 3 4
    1 0 1 x 0 1
    3 0 0 x 1 1
    4 0 0 x 0 0
    
    // Alternative solution
      0 1 2 3 4
    1 0 1 x 0 0
    3 0 0 x 1 0
    4 0 0 x 0 1
    

    Here, x means, there is no solution. Also, column 4 has 2 solutions, so you need to account for that, maybe with a List<int>[a]?

    As to how to fill in those cells exactly:
    Column 0 will hold all zeroes, because it's a solution that can be achieved without a sum.
    For Column i, you want to do i - n (where n is the current number from _arr)

      0 1
    1 0 1   // 1 - 1 == 0, so you put 1 in here
    3 0 0
    4 0 0
    
      0 1 2
    1 0 1 x // 2 - 1 == 1, 1 already has a solution, but you already used the number necessary to solve 1
    3 0 0 x
    4 0 0 x
    

    If you had more than one 1s

      0 1 2
    1 0 1 1 //             1 - 1 == 0
    1 0 0 1 // 2 - 1 == 1, 1 already has a solution
    3 0 0 0
    4 0 0 0
    
    // Alternative solution
      0 1 2
    1 0 0 1 // 2 - 1 == 1, 1 already has a solution
    1 0 1 1 //             1 - 1 == 0
    3 0 0 0
    4 0 0 0
    

    If you need more information on dynamic programming: Knapsack Problem Dynamic Programming Algorithm

    0 讨论(0)
  • 2021-01-07 11:16

    I don't think you can do that using a simple LINQ query. In fact, I'm having a hard time coming up with a simple solution for this using full-blown T-SQL!

    The problem is you're trying to return not single items, but various collections of items based on possible permutations.

    On that note, I do hope someone comes up with such a LINQ query, cos something tells me it would will be awesome. ;)

    0 讨论(0)
  • 2021-01-07 11:27

    It's easy once we have a method to get all subsets of an enumerator/list
    (found here: Answer: Most Elegant Way to Get All Subsets of an Array in C#)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    public static class Program
    {
        static void Main(string[] args)
        {
            var test = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    
            var target = 6;
    
            var matches = from subset in test.SubSetsOf()
                          where subset.Sum() == target
                          select subset;
    
            Console.WriteLine("Numbers: {0}", test.Select(i => i.ToString()).Aggregate((a, n) => a + ", " + n));
            Console.WriteLine("Target: {0}", target);
    
            foreach (var match in matches)
            {
                Console.WriteLine(match.Select(m => m.ToString()).Aggregate((a, n) => a + " + " + n) + " = " + target.ToString());
            }
    
            Console.ReadKey();
        }
    
        public static IEnumerable<IEnumerable<T>> SubSetsOf<T>(this IEnumerable<T> source)
        {
            // Deal with the case of an empty source (simply return an enumerable containing a single, empty enumerable)
            if (!source.Any())
                return Enumerable.Repeat(Enumerable.Empty<T>(), 1);
    
            // Grab the first element off of the list
            var element = source.Take(1);
    
            // Recurse, to get all subsets of the source, ignoring the first item
            var haveNots = SubSetsOf(source.Skip(1));
    
            // Get all those subsets and add the element we removed to them
            var haves = haveNots.Select(set => element.Concat(set));
    
            // Finally combine the subsets that didn't include the first item, with those that did.
            return haves.Concat(haveNots);
        }
    }
    

    Output:

    Numbers: 1, 2, 3, 4, 5, 6, 7, 8, 9
    Target: 6
    1 + 2 + 3 = 6
    1 + 5 = 6
    2 + 4 = 6
    6 = 6
    
    0 讨论(0)
提交回复
热议问题