查找列表中的元素,这些元素一起累加到目标编号

时间:2013-05-17 08:47:13

标签: c# linq

假设我有一个清单:

List<int> _arr = new List<int> {1, 3, 4};

目标为4

我想使用给定列表中的linq将{1, 3}作为1 + 3 = 4{4}作为4 = 4返回。

我该怎么做?

4 个答案:

答案 0 :(得分:10)

一旦我们有一个方法来获取枚举器/列表的所有子集,这很容易 (见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);
    }
}

输出:

Numbers: 1, 2, 3, 4, 5, 6, 7, 8, 9
Target: 6
1 + 2 + 3 = 6
1 + 5 = 6
2 + 4 = 6
6 = 6

答案 1 :(得分:2)

我认为这个问题的最佳解决方案是Dynamic Programming

创建一个二维数组,其维度为[a + 1,_arr.Length]:

  0 1 2 3 4
1
3
4

然后,对于该2D数组中的每一列,填写每个单元格,使列的所有单元格的总和等于列的索引:

  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

这里,x表示没有解决方案。此外,第4列有2个解决方案,因此您需要考虑到这一点,可能需要List<int>[a]

至于如何填写正好的单元格:
0将保留所有零,因为它是一个可以在没有总和的情况下实现的解决方案 对于列i,您想要i - n(其中n是来自_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

如果您有多个1 s

  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

如果您需要有关动态编程的更多信息:Knapsack Problem Dynamic Programming Algorithm

答案 2 :(得分:1)

我认为你不能使用简单的LINQ查询来做到这一点。事实上,我很难用完整的T-SQL为这个提出一个简单的解决方案!

问题是你试图返回的不是单个项目,而是基于可能的排列而返回各种项目集合。

就此而言,我确实希望有人提出这样的LINQ查询,因为有些事情告诉我它会很棒。 ;)

答案 3 :(得分:1)

如果您正在寻求解决此问题的非常好的快速解决方案,那么您并不孤单。

这是一个众所周知的SUBSET SUM问题。我建议你可以在这里查看维基百科:

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

当然,你可以遍历所有可能的子集来寻找你的总和,但要注意有(2 ^ CountOfListElements)可能的组合。如果列表总是很小,那么编码就没有问题。即使是指数时间解决方案也适用于小型集合。