我有一个数组,可以说[1, 2, 3, 4]
。我必须检查一个元素或元素的任何组合是否等于一个特定的数字。
示例
5
,1 + 4 = 5
和2 + 3 = 5
。6
,1 + 2 + 3 = 6
和2 + 4 = 6
一种方法可能是创建数组as in this answer的幂集,并循环遍历该数组。但这不是一个好主意,因为如果元素的数量(即n
)增加,则幂集将占用大量内存。为此,更好的方法是创建特定长度的子集/子数组,并逐个迭代它们。
让我们说k
是子数组的长度
k = 2
应该给我[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
k = 3
应该给我[[1, 2, 3], [1, 2, 4], [2, 3, 4]]
现在的问题是,我将如何创建像上面这样的特定长度的子数组/子集?
答案 0 :(得分:2)
这是子集总和问题的一种变体,或更普遍的说是Knapsack problem。以下解决方案假定:
让我们从一个示例开始:让我们创建一个动态表,在其中我们将尝试通过添加5
中的元素来找到获取[1, 2, 3, 4]
的所有方法:< / p>
在此表中,行代表升序的数组元素,外加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中可以找到关于动态表构造方式的详尽解释。
答案 1 :(得分:0)
这是子集和问题。
对于正整数,可以使用复杂度为O(length * sum)
的动态编程来解决
使数组A的长度为(sum + 1)
,并用零填充,并设置A[0] = 1
对于每个源值v
从A
到A[sum]
遍历数组A[v]
,检查A[i-v]
是否为非零。如果是,请在A[i]
单元格上标记A[i-v] + 1
(编号步长(值)才能到达此单元格)。
如果A[sum]
不为零,并且毕竟包含所需步数的组合,则此和可能由数组元素组成。
如果您还需要跟踪元素,则将其值添加到A[i]
单元中以检索子集。