39. 组合总和

思路:
要求输出具体方案,用回溯法
每个元素可以使用多次
无重复元素,不考虑去重问题。

  1. class Solution {
  2. List<List<Integer>> res = new ArrayList<>();
  3. List<Integer> path = new ArrayList<>();
  4. public List<List<Integer>> combinationSum(int[] candidates, int target) {
  5. dfs(0, candidates, 0, target);
  6. return res;
  7. }
  8. void dfs(int u, int[] a, int sum, int target) {
  9. if (sum >= target) {
  10. if (sum == target)
  11. res.add(new ArrayList<>(path));
  12. return;
  13. }
  14. for (int i = u; i < a.length; i++) {
  15. path.add(a[i]);
  16. dfs(i, a, sum + a[i], target);
  17. path.remove(path.size() - 1);
  18. }
  19. }
  20. }

方法2:完全背包,再求具体方案

  1. class Solution {
  2. List<List<Integer>> res = new ArrayList<>();
  3. int[] a;
  4. boolean[][] f;
  5. List<Integer> path = new ArrayList<>();
  6. public List<List<Integer>> combinationSum(int[] candidates, int target) {
  7. int n = candidates.length;
  8. boolean[][] f = new boolean[n + 1][target + 1];
  9. f[0][0] = true;
  10. for (int i = 1; i <= n; i++) {
  11. int x = candidates[i - 1];
  12. for (int j = 0; j <= target; j++) {
  13. f[i][j] = f[i - 1][j];
  14. if (j >= x)
  15. f[i][j] = f[i][j - x] || f[i][j];
  16. }
  17. }
  18. if (!f[n][target]) return res;
  19. this.f = f;
  20. this.a = candidates;
  21. dfs(n, target);
  22. return res;
  23. }
  24. void dfs(int x, int y) {
  25. if (y == 0) {
  26. res.add(new ArrayList<>(path));
  27. return;
  28. }
  29. if (x == 0) return;
  30. int w = a[x - 1];
  31. if (y >= w && f[x][y - w]) {
  32. path.add(w);
  33. dfs(x, y - w);
  34. path.remove(path.size() - 1);
  35. }
  36. if (f[x - 1][y]) {
  37. dfs(x - 1, y);
  38. }
  39. }
  40. }

40. 组合总和 II

思路:
要求输出具体方案,用回溯法。
每个元素只能使用一次
有重复元素,需要考虑去重

  1. class Solution {
  2. List<List<Integer>> res = new ArrayList<>();
  3. List<Integer> path = new ArrayList<>();
  4. public List<List<Integer>> combinationSum2(int[] candidates, int target) {
  5. Arrays.sort(candidates);
  6. dfs(0, candidates, 0, target);
  7. return res;
  8. }
  9. void dfs(int u, int[] a, int sum, int target) {
  10. if (sum >= target) {
  11. if (sum == target)
  12. res.add(new ArrayList<>(path));
  13. return;
  14. }
  15. for (int i = u; i < a.length; i++) {
  16. //去重
  17. if (i > u && a[i] == a[i - 1])
  18. continue;
  19. path.add(a[i]);
  20. dfs(i + 1, a, sum + a[i], target);
  21. path.remove(path.size() - 1);
  22. }
  23. }
  24. }

216. 组合总和 III

思路:
要求输出具体方案,用回溯法。
选择k个数组合成目标值,结果不能有重复

  1. class Solution {
  2. List<List<Integer>> res = new ArrayList<>();
  3. List<Integer> path = new ArrayList<>();
  4. public List<List<Integer>> combinationSum3(int k, int n) {
  5. dfs(1, k, 0, n);
  6. return res;
  7. }
  8. void dfs(int u, int cnt, int sum, int target) {
  9. if (cnt == 0) {
  10. if (sum == target)
  11. res.add(new ArrayList<>(path));
  12. return;
  13. }
  14. for (int i = u; i <= 9; i++) {
  15. path.add(i);
  16. dfs(i + 1, cnt - 1, sum + i, target);
  17. path.remove(path.size() - 1);
  18. }
  19. }
  20. }

377. 组合总和 Ⅳ

思路:
与前三题都不一样,不是输出具体方案,而是输出方案个数,顺序不同也算不同方案(排列),所以不是完全背包问题求方案数(组合)
状态表示:f[i]表示凑i的所有方案构成的集合
属性:方案数
状态计算:考虑当前方案的最后一个数,可以是nums中的任意一个数
f[i] += f[i - x]如果i >= x成立的话
初始化:凑0的方案有1种,什么都不选。f[0] = 1;

  1. class Solution {
  2. public int combinationSum4(int[] nums, int target) {
  3. int[] f = new int[target + 1];
  4. f[0] = 1;
  5. for (int i = 1; i <= target; i++) {
  6. for (int x : nums) {
  7. if (i >= x)
  8. f[i] += f[i - x];
  9. }
  10. }
  11. return f[target];
  12. }
  13. }