我目前正在尝试编写C#代码,这些代码在求和时找到多个等于指定总数的整数数组。我想找到这些组合,而数组中的每个整数都给出了它可以的范围。
例如,如果我们的总数是10并且我们有一个大小为3的int数组,其中第一个数字可以在1到4之间,第二个数字可以是4和4,第三个是3和6,一些可能的组合是[1 ,3,6],[2,2,6]和[4,2,4]。
什么样的算法有助于解决像这样的问题,能够在最有效的时间内运行?另外,在将此问题转换为C#代码时,我应该记住哪些其他事项?
答案 0 :(得分:1)
我会使用递归来做到这一点。您可以简单地迭代所有可能的值,看看它们是否给出了所需的总和。
假设我们有以下输入模式:
N S
min1 min2 min3 ... minN
max1 max2 max3 ... maxN
为您的例子
如果我们的总数是10并且我们有一个大小为3的int数组,那么第一个 数字可以在1和4之间,第二个和第4个,以及第三个3和 6
它将是:
3 10
1 2 3
4 4 6
我们已阅读输入值。现在,我们只是尝试使用每个可能的数字来解决问题。
我们将有List
来存储当前路径:
static List<int> current = new List<int>();
递归函数非常简单:
private static void Do(int index, int currentSum)
{
if (index == length) // Termination
{
if (currentSum == sum) // If result is a required sum - just output it
Output();
return;
}
// try all possible solutions for current index
for (int i = minValues[index]; i <= maxValues[index]; i++)
{
current.Add(i);
Do(index + 1, currentSum + i); // pass new index and new sum
current.RemoveAt(current.Count() - 1);
}
}
对于非负值,我们也可以包括这样的条件。这是递归改进,它将切断大量不正确的迭代。如果我们已经有currentSum
大于sum
那么继续这个递归分支是没用的:
if (currentSum > sum) return;
实际上,这个算法是一个简单的发现组合,它给出了一个S&#34;问题解决方案有一个区别:minValue[index]
和maxValue[index]
内的内环索引。
以下是我的解决方案的工作IDEOne demo。
答案 1 :(得分:1)
你不能比嵌套for循环/递归做得更好。虽然如果你熟悉3SUM问题,你会知道一个小技巧来减少这种算法的时间复杂度!如果您拥有n
范围,那么在您做出第一个n-1
选择后,您就会知道必须从第n个范围中选择的数字!
我将用一个例子来说明我的建议。
如果我们的总数是10并且我们有一个大小为3的int数组,其中第一个数字可以在1到4之间,第二个数字可以在4和4之间,第三个数字可以在5和6之间
首先让我们处理数据更好一些。我个人喜欢使用从0开始而不是任意数字的范围的想法!所以我们从上限中减去下界:
(1 to 4) -> (0 to 3)
(2 to 4) -> (0 to 2)
(5 to 6) -> (0 to 1)
当然,现在我们需要调整目标总和以反映新的范围。所以我们也从目标总和中减去原始的下界!
TargetSum = 10-1-2-5 = 2
现在我们可以用上限来表示我们的范围,因为它们共享下限!所以范围数组看起来像:
RangeArray = [3,2,1]
让我们对此进行排序(以后会变得更加明显)。所以我们有:
RangeArray = [1,2,3]
大!现在进入算法的牛肉......总结!现在我将使用for
循环,因为它更容易用于示例目的。你将不得不使用递归。耶尔达的代码应该给你一个良好的起点。
result = []
for i from 0 to RangeArray[0]:
SumList = [i]
newSum = TargetSum - i
for j from 0 to RangeArray[1]:
if (newSum-j)>=0 and (newSum-j)<=RangeArray[2] then
finalList = SumList + [j, newSum-j]
result.append(finalList)
注意内循环。这是受3SUM算法启发的。我们利用这样一个事实,即我们知道我们必须从第三个范围中选择的价值(因为它是由我们的前两个选项定义的)。
从这里开始,您必须通过将原始下边界添加到来自相应范围的值来将结果重新映射回原始范围。
请注意,我们现在明白为什么排序RangeList
可能是个好主意。最后一个范围被吸收到secondlast范围的循环中。我们希望最大范围是不循环的范围。
我希望这有助于您入门!如果您需要任何帮助将我的伪代码翻译成c#,请问:)