这是子集和问题的变体,或更一般地说,Knapsack problem。以下解决方案假设:
- 初始数组的所有元素都是严格正数,
- 初始数组可能包含重复元素,
- 如果无法求和,则输出为空数组。
让我们从一个示例开始:让我们创建一个动态表,我们将在其中尝试通过添加来自[1, 2, 3, 4] 的元素来找到获得5 的所有方法:
在此表中,行表示数组的元素,按升序排列,加上0。列从0 到总和5。
在每个单元格中,我们都会问自己,是否可以通过添加当前行和前一行的一个或多个标题来获得该列的标题。
解决方案的数量是其中包含true 的单元格的数量。在这种情况下,有两种解决方案:
1)
绿色单元格是true,因此当前行是解决方案中的最后一个元素。在这种情况下,3 是解决方案的一部分。因此,寻找总和为 5 的子数组的问题变成了寻找总和为5 - 3 的子数组。这是2。这由紫色 arrow 1 表示:向左走五列,向上走 1 行。
在arrow 2 中,我们寻找可以得到2 部分和的子集。在这种情况下,我们得到两个感谢 2 元素。所以跟着arrow 2,我们往上走一排,往左边走两排。
使用arrow 3,我们到达第一列的第一个单元格,对应于5 - 3 - 2,即0。
2)
我们可以采取的另一条路径从红细胞开始:
如你所见,[1, 2, 3, 4] 出 5 的问题,变成了一个新的更小的问题,即从[1, 2, 3] 出 1,然后是 [1, 2] 出 1,最后是 `1 出 1 .
让我们创建并填充动态表:
var dynamicTable: [[Bool]] =
Array(repeating: Array(repeating: false, count: sum + 1),
count: array.count + 1)
//All of the elements of the first column are true
//since we can always make a zero sum out of not elements
for i in 0...array.count {
dynamicTable[i][0] = true
}
for row in 1...array.count {
for column in 1...sum {
if column < array[row - 1] {
dynamicTable[row][column] = dynamicTable[row - 1][column]
} else {
if dynamicTable[row - 1][column] {
dynamicTable[row][column] = true
} else {
dynamicTable[row][column] = dynamicTable[row - 1][column - array[row - 1]]
}
}
}
}
让我们找出通向总和的所有路径:
var solutions = [[Int]]()
func getSubArraysWithTheSum(arr: [Int], row: Int, currentSum: Int, currentSolution: [Int]) {
//The following block will be executed when
//we reach the first cell in the first column
if row == 0,
currentSum == 0
{
solutions.append(currentSolution)
//notice the return to exit the scope
return
}
//The following block will be executed if
//the current cell is NOT used to reach the sum
if dynamicTable[row - 1][currentSum]
{
getSubArraysWithTheSum(arr: arr,
row: row - 1,
currentSum: currentSum,
currentSolution: currentSolution)
}
//The following block will be executed if
//the current cell IS used to reach the sum
if currentSum >= arr[row - 1],
dynamicTable[row - 1][currentSum - arr[row - 1]]
{
getSubArraysWithTheSum(arr: arr,
row: row - 1,
currentSum: currentSum - arr[row - 1],
currentSolution: currentSolution + [arr[row - 1]])
}
}
整个函数如下所示:
func getSubArrays(from array: [Int], withSum sum: Int) -> [[Int]] {
guard array.allSatisfy({ $0 > 0 }) else {
fatalError("All the elements of the array must be strictly positive")
}
guard array.count > 0, sum > 0 else {
return []
}
var solutions = [[Int]]()
var dynamicTable: [[Bool]] =
Array(repeating: Array(repeating: false, count: sum + 1),
count: array.count + 1)
//All of the elements of the first column are true
//since we can always make a zero sum out of not elements
for i in 0...array.count {
dynamicTable[i][0] = true
}
for row in 1...array.count {
for column in 1...sum {
if column < array[row - 1] {
dynamicTable[row][column] = dynamicTable[row - 1][column]
} else {
if dynamicTable[row - 1][column] {
dynamicTable[row][column] = true
} else {
dynamicTable[row][column] = dynamicTable[row - 1][column - array[row - 1]]
}
}
}
}
func getSubArraysWithTheSum(arr: [Int], row: Int, currentSum: Int, currentSolution: [Int]) {
//The following block will be executed when
//we reach the first cell in the first column
if row == 0,
currentSum == 0
{
solutions.append(currentSolution)
return
}
//The following block will be executed if
//the current cell is NOT used to reach the sum
if dynamicTable[row - 1][currentSum]
{
getSubArraysWithTheSum(arr: arr,
row: row - 1,
currentSum: currentSum,
currentSolution: currentSolution)
}
//The following block will be executed if
//the current cell IS used to reach the sum
if currentSum >= arr[row - 1],
dynamicTable[row - 1][currentSum - arr[row - 1]]
{
getSubArraysWithTheSum(arr: arr,
row: row - 1,
currentSum: currentSum - arr[row - 1],
currentSolution: currentSolution + [arr[row - 1]])
}
}
getSubArraysWithTheSum(arr: array, row: array.count , currentSum: sum, currentSolution: [])
return solutions
}
这里有一些测试用例:
getSubArrays(from: [3, 1, 4, 2], withSum: 5) //[[3, 2], [4, 1]]
getSubArrays(from: [1, 2, 2, 4], withSum: 3) //[[2, 1], [2, 1]]
getSubArrays(from: [7, 3, 4, 5, 6, 1], withSum: 9) //[[5, 3, 1], [5, 4], [6, 3]]
getSubArrays(from: [3], withSum: 3) //[[3]]
getSubArrays(from: [5], withSum: 10) //[]
getSubArrays(from: [1, 2], withSum: 0) //[]
getSubArrays(from: [], withSum: 4) //[]
此解决方案的灵感来自 Sumit Ghosh 的贡献 here。 this video 详细解释了动态表是如何构建的。