一.判断是否有和为sum的组合,动规法,O(n^2)
// 判断是否有和为n的组合,动规法,O(n^2)
public static boolean findSum(int[] a, int n) {
boolean[] dp = new boolean[n + 1];
for (int i = 0; i < a.length; i++) {
if (a[i] > n) {
continue;
}
for (int j = n; j >= a[i]; j--) {
if (dp[j - a[i]]) {
dp[j] = true;
}
}
dp[a[i]] = true;
if (dp[n]) {
return true;
}
}
return false;
}
二、 有几种和为n的组合,动规法,O(n^2)
// 有几种和为n的组合,动规法,O(n^2)
public static int findSum2(int[] a, int n) {
int[] dp = new int[n + 1];
dp[0] = 1;
for (int i = 0; i < a.length; i++) {
if (a[i] > n) {
continue;
}
for (int j = n; j >= a[i]; j--) {
if (dp[j - a[i]] > 0) {
dp[j] += dp[j - a[i]];
}
}
if (dp[a[i]] == 0) {
dp[a[i]] = 1;
}
}
return dp[n];
}
三.查找和为sum的所有组合
方法一:打印和为n的所有组合,DFS法,O(2^n)
// 查找和为n的所有组合,DFS法,O(2^n)
public static void dfs(int current, int sum, int n, int[] questions, ArrayList<Integer> path,
HashSet<List<Integer>> result) {
if (sum == n) {
result.add(new ArrayList<>(path));
return;
}
if (sum > n) {
return;
}
for (int i = current; i < questions.length; i++) {
path.add(questions[i]);
dfs(i + 1, sum + questions[i], n, questions, path, result);
path.remove(path.size() - 1);
}
}
方法二:打印和为n的组合,动规法,O(n^2
// 打印和为n的组合,动规法,O(n^2)
public static List<List<Object>> findSums(int[] a, int n) {
boolean[] dp = new boolean[n + 1];
List<List<Object>>[] list = new ArrayList[n + 1];
for (int i = 0; i < list.length; i++) {
list[i] = new ArrayList<>();
}
List<Object> temp = new ArrayList<>();
for (int i = 0; i < a.length; i++) {
if (a[i] > n) {
continue;
}
for (int j = n; j >= a[i]; j--) {
if (dp[j - a[i]]) {
dp[j] = true;
for (List<Object> arrayList : list[j - a[i]]) {
temp = new ArrayList<>(arrayList);
temp.add(a[i]);
list[j].add(new ArrayList<>(temp));
}
}
}
dp[a[i]] = true;
temp.clear();
temp.add(a[i]);
list[a[i]].add(new ArrayList<>(temp));
}
return list[n];
}
方法三:打印和为n的组合,迭代法,<O(n*2^n)
// 打印和为n的组合,迭代法,<O(n*2^n)
public static List<List<Integer>> findSums(int[] a, int n) {
List<List<Integer>> result = new ArrayList<>();
int len = a.length;
int bit = 1 << len;
for (int i = 1; i < bit; i++)// 从1循环到2^N
{
int sum = 0;
List<Integer> tmp = new ArrayList<>();
for (int j = 0; j < len; j++) {
if ((i & 1 << j) != 0)// 用i与2^j进行位与运算,若结果不为0,则表示第j位不为0,从数组中取出第j个数
{
sum += a[j];
tmp.add(a[j]);
}
}
if (sum == n) {
result.add(tmp);
}
}
return result;
}
方法四:从长度为len的数组里选出m个数使和为固定值n,<O(n*2^n)
// 从长度为len的数组里选出m个数使和为固定值n,<O(n*2^n)
public static List<List<Integer>> CalSum(int[] a, int n, int m) {
List<List<Integer>> result = new ArrayList<>();
int len = a.length;
int bit = 1 << len;
for (int i = 1; i < bit; i++)// 从1循环到2^N
{
int sum = 0;
List<Integer> tmp = new ArrayList<>();
if (NumOf1(i) == m) {// 判断是否为每个数
for (int j = 0; j < len; j++) {
if ((i & 1 << j) != 0)// 用i与2^j进行位与运算,若结果不为0,则表示第j位不为0,从数组中取出第j个数
{
sum += a[j];
tmp.add(a[j]);
}
}
if (sum == n) {
result.add(tmp);
}
}
}
return result;
}
public static int NumOf1(int num) {
int count = 0;
while (num > 0) {
num = num & (num - 1);
count++;
}
return count;
}
测试主程序:
用例:
11
9 49 1 2 48 50 47 3 1 2 41
输出:
是否可以:true
动规不去重打印: 大小:37
有几种:37
[[1, 2, 9, 41, 47], [1, 2, 9, 41, 47], [1, 2, 9, 41, 47], [1, 2, 9, 41, 47], [3, 9, 41, 47], [1, 1, 9, 41, 48], [2, 9, 41, 48], [2, 9, 41, 48], [1, 2, 2, 47, 48], [1, 2, 2, 47, 48], [1, 1, 3, 47, 48], [2, 3, 47, 48], [2, 3, 47, 48], [1, 9, 41, 49], [1, 9, 41, 49], [1, 1, 2, 47, 49], [1, 1, 2, 47, 49], [2, 2, 47, 49], [1, 3, 47, 49], [1, 3, 47, 49], [1, 2, 48, 49], [1, 2, 48, 49], [1, 2, 48, 49], [1, 2, 48, 49], [3, 48, 49], [1, 1, 2, 2, 3, 41, 50], [9, 41, 50], [1, 2, 47, 50], [1, 2, 47, 50], [1, 2, 47, 50], [1, 2, 47, 50], [3, 47, 50], [1, 1, 48, 50], [2, 48, 50], [2, 48, 50], [1, 49, 50], [1, 49, 50]]
迭代不去重打印: 大小:37
[[1, 2, 9, 41, 47], [1, 2, 9, 41, 47], [1, 2, 9, 41, 47], [1, 2, 9, 41, 47], [3, 9, 41, 47], [1, 1, 9, 41, 48], [2, 9, 41, 48], [2, 9, 41, 48], [1, 2, 2, 47, 48], [1, 2, 2, 47, 48], [1, 1, 3, 47, 48], [2, 3, 47, 48], [2, 3, 47, 48], [1, 9, 41, 49], [1, 9, 41, 49], [1, 1, 2, 47, 49], [1, 1, 2, 47, 49], [2, 2, 47, 49], [1, 3, 47, 49], [1, 3, 47, 49], [1, 2, 48, 49], [1, 2, 48, 49], [1, 2, 48, 49], [1, 2, 48, 49], [3, 48, 49], [1, 1, 2, 2, 3, 41, 50], [9, 41, 50], [1, 2, 47, 50], [1, 2, 47, 50], [1, 2, 47, 50], [1, 2, 47, 50], [3, 47, 50], [1, 1, 48, 50], [2, 48, 50], [2, 48, 50], [1, 49, 50], [1, 49, 50]]
m个和为n:
[[3, 9, 41, 47], [2, 9, 41, 48], [2, 9, 41, 48], [2, 3, 47, 48], [2, 3, 47, 48], [1, 9, 41, 49], [1, 9, 41, 49], [2, 2, 47, 49], [1, 3, 47, 49], [1, 3, 47, 49], [1, 2, 48, 49], [1, 2, 48, 49], [1, 2, 48, 49], [1, 2, 48, 49], [1, 2, 47, 50], [1, 2, 47, 50], [1, 2, 47, 50], [1, 2, 47, 50], [1, 1, 48, 50]]
DFS去重打印:大小:20
[1, 1, 2, 47, 49] [1, 1, 3, 47, 48] [2, 9, 41, 48] [1, 49, 50] [1, 2, 2, 47, 48] [3, 9, 41, 47] [2, 48, 50] [3, 48, 49] [3, 47, 50] [1, 2, 47, 50] [1, 3, 47, 49] [1, 1, 9, 41, 48] [2, 2, 47, 49] [2, 3, 47, 48] [1, 1, 48, 50] [1, 2, 48, 49] [1, 2, 9, 41, 47] [9, 41, 50] [1, 1, 2, 2, 3, 41, 50] [1, 9, 41, 49]
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] questions = new int[n];
for (int i = 0; i < questions.length; i++) {
questions[i] = sc.nextInt();
}
System.out.println("是否可以:" + findSum(questions, 100));
System.out.println("有几种:" + findSum2(questions, 100));
System.out.println("动规不去重打印: " + "大小:" + findSums(questions, 100).size() + findSums(questions, 100));
System.out.println("迭代不去重打印: " + "大小:" + findSums2(questions, 100).size() + " " + findSums2(questions, 100));
System.out.println("m个和为n:" + CalSum(questions, 100, 4));
HashSet<List<Integer>> result = new HashSet<>();
dfs(0, 0, 100, questions, new ArrayList<Integer>(), result);
System.out.println("DFS去重打印:" + "大小:" + result.size());
for (List<Integer> i : result) {
System.out.print(i + " ");
}
}