Skip to content

Commit

Permalink
Add the section of subset sum problem. (krahets#558)
Browse files Browse the repository at this point in the history
  • Loading branch information
krahets authored Jun 20, 2023
1 parent 9fc1a0b commit 0e2ddba
Show file tree
Hide file tree
Showing 16 changed files with 821 additions and 5 deletions.
57 changes: 57 additions & 0 deletions codes/cpp/chapter_backtracking/subset_sum_i.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* File: subset_sum_i.cpp
* Created Time: 2023-06-21
* Author: Krahets ([email protected])
*/

#include "../utils/common.hpp"

/* 回溯算法:子集和 I */
void backtrack(vector<int> &state, int target, vector<int> &choices, int start, vector<vector<int>> &res) {
// 子集和等于 target 时,记录解
if (target == 0) {
res.push_back(state);
return;
}
// 遍历所有选择
// 剪枝二:从 start 开始遍历,避免生成重复子集
for (int i = start; i < choices.size(); i++) {
// 剪枝一:若子集和超过 target ,则直接结束循环
// 这是因为数组已排序,后边元素更大,子集和一定超过 target
if (target - choices[i] < 0) {
break;
}
// 尝试:做出选择,更新 target, start
state.push_back(choices[i]);
// 进行下一轮选择
backtrack(state, target - choices[i], choices, i, res);
// 回退:撤销选择,恢复到之前的状态
state.pop_back();
}
}

/* 求解子集和 I */
vector<vector<int>> subsetSumI(vector<int> &nums, int target) {
vector<int> state; // 状态(子集)
sort(nums.begin(), nums.end()); // 对 nums 进行排序
int start = 0; // 遍历起始点
vector<vector<int>> res; // 结果列表(子集列表)
backtrack(state, target, nums, start, res);
return res;
}

/* Driver Code */
int main() {
vector<int> nums = {3, 4, 5};
int target = 9;

vector<vector<int>> res = subsetSumI(nums, target);

cout << "输入数组 nums = ";
printVector(nums);
cout << "target = " << target << endl;
cout << "所有和等于 " << target << " 的子集 res = " << endl;
printVectorMatrix(res);

return 0;
}
54 changes: 54 additions & 0 deletions codes/cpp/chapter_backtracking/subset_sum_i_naive.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* File: subset_sum_i_naive.cpp
* Created Time: 2023-06-21
* Author: Krahets ([email protected])
*/

#include "../utils/common.hpp"

/* 回溯算法:子集和 I */
void backtrack(vector<int> &state, int target, int total, vector<int> &choices, vector<vector<int>> &res) {
// 子集和等于 target 时,记录解
if (total == target) {
res.push_back(state);
return;
}
// 遍历所有选择
for (size_t i = 0; i < choices.size(); i++) {
// 剪枝:若子集和超过 target ,则跳过该选择
if (total + choices[i] > target) {
continue;
}
// 尝试:做出选择,更新元素和 total
state.push_back(choices[i]);
// 进行下一轮选择
backtrack(state, target, total + choices[i], choices, res);
// 回退:撤销选择,恢复到之前的状态
state.pop_back();
}
}

/* 求解子集和 I(包含重复子集) */
vector<vector<int>> subsetSumINaive(vector<int> &nums, int target) {
vector<int> state; // 状态(子集)
int total = 0; // 子集和
vector<vector<int>> res; // 结果列表(子集列表)
backtrack(state, target, total, nums, res);
return res;
}

/* Driver Code */
int main() {
vector<int> nums = {3, 4, 5};
int target = 9;

vector<vector<int>> res = subsetSumINaive(nums, target);

cout << "输入数组 nums = ";
printVector(nums);
cout << "target = " << target << endl;
cout << "所有和等于 " << target << " 的子集 res = " << endl;
printVectorMatrix(res);

return 0;
}
62 changes: 62 additions & 0 deletions codes/cpp/chapter_backtracking/subset_sum_ii.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* File: subset_sum_ii.cpp
* Created Time: 2023-06-21
* Author: Krahets ([email protected])
*/

#include "../utils/common.hpp"

/* 回溯算法:子集和 II */
void backtrack(vector<int> &state, int target, vector<int> &choices, int start, vector<vector<int>> &res) {
// 子集和等于 target 时,记录解
if (target == 0) {
res.push_back(state);
return;
}
// 遍历所有选择
// 剪枝二:从 start 开始遍历,避免生成重复子集
// 剪枝三:从 start 开始遍历,避免重复选择同一元素
for (int i = start; i < choices.size(); i++) {
// 剪枝一:若子集和超过 target ,则直接结束循环
// 这是因为数组已排序,后边元素更大,子集和一定超过 target
if (target - choices[i] < 0) {
break;
}
// 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过
if (i > start && choices[i] == choices[i - 1]) {
continue;
}
// 尝试:做出选择,更新 target, start
state.push_back(choices[i]);
// 进行下一轮选择
backtrack(state, target - choices[i], choices, i + 1, res);
// 回退:撤销选择,恢复到之前的状态
state.pop_back();
}
}

/* 求解子集和 II */
vector<vector<int>> subsetSumII(vector<int> &nums, int target) {
vector<int> state; // 状态(子集)
sort(nums.begin(), nums.end()); // 对 nums 进行排序
int start = 0; // 遍历起始点
vector<vector<int>> res; // 结果列表(子集列表)
backtrack(state, target, nums, start, res);
return res;
}

/* Driver Code */
int main() {
vector<int> nums = {4, 4, 5};
int target = 9;

vector<vector<int>> res = subsetSumII(nums, target);

cout << "输入数组 nums = ";
printVector(nums);
cout << "target = " << target << endl;
cout << "所有和等于 " << target << " 的子集 res = " << endl;
printVectorMatrix(res);

return 0;
}
55 changes: 55 additions & 0 deletions codes/java/chapter_backtracking/subset_sum_i.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* File: subset_sum_i.java
* Created Time: 2023-06-21
* Author: Krahets ([email protected])
*/

package chapter_backtracking;

import java.util.*;

public class subset_sum_i {
/* 回溯算法:子集和 I */
static void backtrack(List<Integer> state, int target, int[] choices, int start, List<List<Integer>> res) {
// 子集和等于 target 时,记录解
if (target == 0) {
res.add(new ArrayList<>(state));
return;
}
// 遍历所有选择
// 剪枝二:从 start 开始遍历,避免生成重复子集
for (int i = start; i < choices.length; i++) {
// 剪枝一:若子集和超过 target ,则直接结束循环
// 这是因为数组已排序,后边元素更大,子集和一定超过 target
if (target - choices[i] < 0) {
break;
}
// 尝试:做出选择,更新 target, start
state.add(choices[i]);
// 进行下一轮选择
backtrack(state, target - choices[i], choices, i, res);
// 回退:撤销选择,恢复到之前的状态
state.remove(state.size() - 1);
}
}

/* 求解子集和 I */
static List<List<Integer>> subsetSumI(int[] nums, int target) {
List<Integer> state = new ArrayList<>(); // 状态(子集)
Arrays.sort(nums); // 对 nums 进行排序
int start = 0; // 遍历起始点
List<List<Integer>> res = new ArrayList<>(); // 结果列表(子集列表)
backtrack(state, target, nums, start, res);
return res;
}

public static void main(String[] args) {
int[] nums = { 3, 4, 5 };
int target = 9;

List<List<Integer>> res = subsetSumI(nums, target);

System.out.println("输入数组 nums = " + Arrays.toString(nums) + ", target = " + target);
System.out.println("所有和等于 " + target + " 的子集 res = " + res);
}
}
53 changes: 53 additions & 0 deletions codes/java/chapter_backtracking/subset_sum_i_naive.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* File: subset_sum_i_naive.java
* Created Time: 2023-06-21
* Author: Krahets ([email protected])
*/

package chapter_backtracking;

import java.util.*;

public class subset_sum_i_naive {
/* 回溯算法:子集和 I */
static void backtrack(List<Integer> state, int target, int total, int[] choices, List<List<Integer>> res) {
// 子集和等于 target 时,记录解
if (total == target) {
res.add(new ArrayList<>(state));
return;
}
// 遍历所有选择
for (int i = 0; i < choices.length; i++) {
// 剪枝:若子集和超过 target ,则跳过该选择
if (total + choices[i] > target) {
continue;
}
// 尝试:做出选择,更新元素和 total
state.add(choices[i]);
// 进行下一轮选择
backtrack(state, target, total + choices[i], choices, res);
// 回退:撤销选择,恢复到之前的状态
state.remove(state.size() - 1);
}
}

/* 求解子集和 I(包含重复子集) */
static List<List<Integer>> subsetSumINaive(int[] nums, int target) {
List<Integer> state = new ArrayList<>(); // 状态(子集)
int total = 0; // 子集和
List<List<Integer>> res = new ArrayList<>(); // 结果列表(子集列表)
backtrack(state, target, total, nums, res);
return res;
}

public static void main(String[] args) {
int[] nums = { 3, 4, 5 };
int target = 9;

List<List<Integer>> res = subsetSumINaive(nums, target);

System.out.println("输入数组 nums = " + Arrays.toString(nums) + ", target = " + target);
System.out.println("所有和等于 " + target + " 的子集 res = " + res);
System.out.println("请注意,该方法输出的结果包含重复集合");
}
}
60 changes: 60 additions & 0 deletions codes/java/chapter_backtracking/subset_sum_ii.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* File: subset_sum_ii.java
* Created Time: 2023-06-21
* Author: Krahets ([email protected])
*/

package chapter_backtracking;

import java.util.*;

public class subset_sum_ii {
/* 回溯算法:子集和 II */
static void backtrack(List<Integer> state, int target, int[] choices, int start, List<List<Integer>> res) {
// 子集和等于 target 时,记录解
if (target == 0) {
res.add(new ArrayList<>(state));
return;
}
// 遍历所有选择
// 剪枝二:从 start 开始遍历,避免生成重复子集
// 剪枝三:从 start 开始遍历,避免重复选择同一元素
for (int i = start; i < choices.length; i++) {
// 剪枝一:若子集和超过 target ,则直接结束循环
// 这是因为数组已排序,后边元素更大,子集和一定超过 target
if (target - choices[i] < 0) {
break;
}
// 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过
if (i > start && choices[i] == choices[i - 1]) {
continue;
}
// 尝试:做出选择,更新 target, start
state.add(choices[i]);
// 进行下一轮选择
backtrack(state, target - choices[i], choices, i + 1, res);
// 回退:撤销选择,恢复到之前的状态
state.remove(state.size() - 1);
}
}

/* 求解子集和 II */
static List<List<Integer>> subsetSumII(int[] nums, int target) {
List<Integer> state = new ArrayList<>(); // 状态(子集)
Arrays.sort(nums); // 对 nums 进行排序
int start = 0; // 遍历起始点
List<List<Integer>> res = new ArrayList<>(); // 结果列表(子集列表)
backtrack(state, target, nums, start, res);
return res;
}

public static void main(String[] args) {
int[] nums = { 4, 4, 5 };
int target = 9;

List<List<Integer>> res = subsetSumII(nums, target);

System.out.println("输入数组 nums = " + Arrays.toString(nums) + ", target = " + target);
System.out.println("所有和等于 " + target + " 的子集 res = " + res);
}
}
48 changes: 48 additions & 0 deletions codes/python/chapter_backtracking/subset_sum_i.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
File: subset_sum_i.py
Created Time: 2023-06-17
Author: Krahets ([email protected])
"""


def backtrack(
state: list[int], target: int, choices: list[int], start: int, res: list[list[int]]
):
"""回溯算法:子集和 I"""
# 子集和等于 target 时,记录解
if target == 0:
res.append(list(state))
return
# 遍历所有选择
# 剪枝二:从 start 开始遍历,避免生成重复子集
for i in range(start, len(choices)):
# 剪枝一:若子集和超过 target ,则直接结束循环
# 这是因为数组已排序,后边元素更大,子集和一定超过 target
if target - choices[i] < 0:
break
# 尝试:做出选择,更新 target, start
state.append(choices[i])
# 进行下一轮选择
backtrack(state, target - choices[i], choices, i, res)
# 回退:撤销选择,恢复到之前的状态
state.pop()


def subset_sum_i(nums: list[int], target: int) -> list[list[int]]:
"""求解子集和 I"""
state = [] # 状态(子集)
nums.sort() # 对 nums 进行排序
start = 0 # 遍历起始点
res = [] # 结果列表(子集列表)
backtrack(state, target, nums, start, res)
return res


"""Driver Code"""
if __name__ == "__main__":
nums = [3, 4, 5]
target = 9
res = subset_sum_i(nums, target)

print(f"输入数组 nums = {nums}, target = {target}")
print(f"所有和等于 {target} 的子集 res = {res}")
Loading

0 comments on commit 0e2ddba

Please sign in to comment.