带范围的数字的推荐组合算法
本文关键字:组合 算法 数字 范围 | 更新日期: 2023-09-27 18:08:19
我目前正在尝试编写c#代码,查找多个整数数组,当它们相加时等于指定的总数。我想找到这些组合,而数组中的每个整数被给定一个范围,它可以。
例如,如果我们的总数是10,并且我们有一个大小为3的int数组,其中第一个数字可以在1到4之间,第二个数字可以在2到4之间,第三个数字可以在3到6之间,则可能的组合是[1,3,6],[2,2,6]和[4,2,4]。
什么样的算法可以帮助解决这样一个问题,可以在他们最有效的时间内运行?此外,在将这个问题转换为c#代码时,我还应该记住哪些其他事情?
我将使用递归来做到这一点。您可以简单地遍历所有可能的值,看看它们是否给出所需的和。
输入让我们假设我们有以下输入模式:
N S
min1 min2 min3 ... minN
max1 max2 max3 ... maxN
对于您的示例
,如果我们的总数是10,并且我们有一个大小为3的int数组,其中第一个数字可以在1和4之间,第二个2和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的组合"问题的解决方案,只有一个区别:minValue[index]
和maxValue[index]
内循环索引。
这是我的解决方案的工作IDEOne演示。
没有比嵌套for循环/递归更好的了。虽然如果你熟悉3SUM问题,你会知道一个小技巧来减少这种算法的时间复杂度!如果您有n
范围,那么您知道在您做出第一个n-1
选择后,您必须从第n个范围中选择什么数字!
如果我们的总数是10,我们有一个大小为3的int数组,其中第一个数字可以在1到4之间,第二个数字可以在2到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
循环,因为它更容易用于示例目的。你必须使用递归。Yeldar的代码应该给你一个很好的起点。
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
排序可能是一个好主意。最后一个范围被吸收到第二个范围的循环中。我们希望最大的范围是不循环的范围。
我希望这能帮助你开始!如果你需要任何帮助把我的伪代码翻译成c#就问:)