鉴于下面的降序唯一数字列表(3,2,1),我希望生成由这些数字组成的所有序列,最大总和。
让我们说总和应该低于10.然后我所追求的序列是:
3 3 3
3 3 2 1
3 3 2
3 3 1 1 1
3 3 1 1
3 3 1
3 3
3 2 2 2
3 2 2 1 1
3 2 2 1
3 2 2
3 2 1 1 1 1
3 2 1 1 1
3 2 1 1
3 2 1
3 2
3 1 1 1 1 1 1
3 1 1 1 1 1
3 1 1 1 1
3 1 1 1
3 1 1
3 1
3
2 2 2 2 1
2 2 2 2
2 2 2 1 1 1
2 2 2 1 1
2 2 2 1
2 2 2
2 2 1 1 1 1 1
2 2 1 1 1 1
2 2 1 1 1
2 2 1 1
2 2 1
2 2
2 1 1 1 1 1 1 1
2 1 1 1 1 1 1
2 1 1 1 1 1
2 1 1 1 1
2 1 1 1
2 1 1
2 1
2
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1
1 1 1 1
1 1 1
1 1
1
我确信有一种“标准”方式来生成它。
我想过使用linq但是无法弄明白。 此外,我正在尝试基于堆栈的方法,但我仍然没有成功。
有什么想法吗?
答案 0 :(得分:2)
我认为这是最简单的递归写法,纯粹的LINQ并不是那么好用。所以最好将它作为一个普通的递归函数来实现。将最大总数中剩余的数量作为参数传递,每次添加en元素时,递归调用函数,适当减少总数:
IEnumerable<IEnumerable<int>> sequences(IEnumerable<int> set, int maxTotal)
{
foreach (int i in set.Where(x => x <= maxTotal))
{
yield return new int[] { i };
foreach (var s in sequences(set.Where(x => x <= i), maxTotal - i))
yield return (new int[] { i }).Concat(s);
}
}
void Run()
{
foreach (var z in sequences(new int[] { 3, 2, 1 }, 10))
{
Console.WriteLine(
string.Join(" ", z.Select(x => x.ToString()).ToArray())
);
}
}
结果:
3
3 3
3 3 3
3 3 3 1
3 3 2
3 3 2 2
3 3 2 1
3 3 2 1 1
3 3 1
3 3 1 1
3 3 1 1 1
...
1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
请注意,这不是最有效的解决方案。
答案 1 :(得分:0)
我认为您可以通过构建树来解决这个问题。 在每个节点上,您都有一个值列表。假设此列表的总和小于所需的总和 - 让我们称之为S - ,您最多可以为此节点构建三个子节点:一个通过向此节点的列表添加1,一个通过添加2和一个通过添加3建立孩子的条件是新列表的总和仍然小于S. 最后,即当您无法生成新节点时,您的所有序列都在树的节点中。
编辑:在C#中,我的糟糕解释就是这样:第一
public class Node
{
public Node()
{
Children = new List<Node>();
}
public static int SumMax { get; set; }
public List<int> Values { get; set; }
public List<Node> Children { get; set; }
public void AddChild(int data)
{
if (Values.Sum() + data < SumMax)
{
Node child = new Node();
child.Values = new List<int>(Values);
child.Values.Add(data);
Children.Add(child);
for (int = data; i < 4; i++)
{
child.AddChild(i);
}
}
}
public void FillSequences(List<List<int>> sequences)
{
if (Values.Count != 0)
{
sequences.Add(Values);
}
foreach (Node child in Children)
{
child.FillSequences(sequences);
}
}
}
然后是主要的:
void Main()
{
Node.SumMax = 10;
Node root = new Node();
root.Values = new List<int>();
for (int i = 1; i < 4; i++)
root.AddChild(i);
List<List<int>> sequences = new List<List<int>>();
root.FillSequences(sequences);
//here you've got your sequences results in "sequences" and you can do what you want
}
我不知道它是否足够标准,但它大致完成了工作。我希望它符合你的需要......
编辑:为了避免生成相同的序列,我们可以“命令”树:节点无法生成值低于其值的子节点。因此,在AddChild方法中,我们在“data”而不是1处开始循环。
答案 2 :(得分:0)
我不知道这是否是“标准”,但从底部开始并开始工作并不罕见。
有1种方法可以制作1。
有两种方法可以制作2,分为3类:以1开头的那些,以2开头的那些,以及以3开头的那些。最后一类当然是空的。
要计算制作N的方法,请考虑使N-1从1开始的方法,使N-2以2或1开始的方法,以及使N-3以3,2开始的方法,或者1。
答案 3 :(得分:0)
在我看来,“最简单”(如果不是最有效的)这样做的方法就是只取你的数字设置,然后取出它的所有排列,然后过滤出总和高于截止点。
答案 4 :(得分:0)
好像你对partitions感兴趣,有点麻烦。这个功能起到了作用。基本上,在它们的总和小于目标总和时继续向堆栈添加数字,然后在每一步打印堆栈。
class Program
{
static void Main()
{
GeneratePossibilites(new int[] {3, 2, 1}, 10, 0, new List<int>());
}
static void GeneratePossibilites(int[] array, int maxSum, int crSum, List<int> stack)
{
for (int i = 0; i < stack.Count; ++i )
Console.Write(array[ stack[i] ].ToString() + " ");
int start = 0;
if (stack.Count > 0)
{
start = stack[stack.Count - 1];
Console.WriteLine();
}
for (int i = start; i < array.Length; ++i)
if (crSum + array[i] < maxSum)
{
stack.Add(i);
GeneratePossibilites(array, maxSum, crSum + array[i], stack);
stack.RemoveAt(stack.Count - 1);
}
}
}
我不确定您是否需要特定格式的输出,但您可以使用此答案或其他答案。