Those things about retrospective

In the process of lc brushing, there are a kind of questions that can be dealt with by retrospective thinking, such as full permutation, subset, combination, segmentation palindrome strings and so on. There are many simple explanations about backtracking algorithm on the internet, which are easy to get started. A simple explanation is to try all the possibilities once, record the success, and go back to the previous test path and continue to try in other directions until all the possibilities have been tried and return to the required records. The following is a summary of how to solve these problems in a unified way.
Firstly, we list the general templates to solve this kind of problems. It doesn't matter if you don't understand them. Later, we will explain them one by one according to the examples.

public List<List<Integer>> backtracking(int[] candidates, int target) {
        List<List<Integer>> ans = new ArrayList<>();
        // Necessary. Necessary when required
        Arrays.sort(candidates);
        this.recursion(candidates, 0, target, new ArrayList<>(), ans, new boolean[candidates.length]);
        return ans;
    }

	/**
     * Recursive function
     * @param nums Given an array, it may also be int n
     * @param start   Necessary, starting position of each cycle to prevent digital reuse
     * @param remained  Necessary. Some questions give a target value. sum is only equal to the target value and so on.
     * @param list  Temporary list, which stores part of the result set
     * @param ans Result set
     * @param used Necessity, to repeat what you need
     */
    private void recursion(int[] nums, int start, int remained, List<Integer> list, List<List<Integer>> ans, boolean[] used) {
        if (remained == 0) {
            ans.add(new ArrayList<>(list));
        } else {
            for (int i = start; i < nums.length; i++) {
                if (remained < nums[i]) {
                    break;
                }
                if (i > start && nums[i] == nums[i-1]) continue;
                list.add(nums[i]);
                recursion(nums, i+1,remained - nums[i], list, ans, used);
                list.remove(list.size()-1);
            }
        }
    }

First, look at question lc78:
Given a set of integer arrays nums without repeating elements, all possible subsets (power sets) of the array are returned.
Description: Unset cannot contain duplicate subsets.
Example:
Input: nums = [1,2,3]
Output:
[[3],[1], [2], [1,2,3], [1,3], [2,3], [1,2], []]

    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        recursion(nums, 0, new ArrayList<>(), ans);
        return ans;
    }

    private void recursion(int[] nums, int start, List<Integer> list, List<List<Integer>> ans) {
        ans.add(new ArrayList<>(list));
        for (int i=start; i<nums.length; i++) {
            list.add(nums[i]);
            recursion(nums, i+1, list, ans);
            list.remove(list.size()-1);
        }
    }

This is the simplest retrospective processing. There is no duplication in a given sequence and no duplication in the result set.

Two, lc90
Given an integer array nums that may contain repetitive elements, all possible subsets (power sets) of the array are returned.
Description: Unset cannot contain duplicate subsets.
Example:
Input: [1,2,2]
Output:
[ [2], [1], [1,2,2], [2,2], [1,2], []]

public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> ans = new ArrayList<>();
        recursion(nums, 0, new ArrayList<>(), ans, new boolean[nums.length]);
        return ans;
    }

    private void recursion(int[] nums, int start, List<Integer> list, List<List<Integer>> ans, boolean[] used) {
        ans.add(new ArrayList<>(list));
        for (int i=start; i<nums.length; i++) {
            if (!used[i]) {
                // Repeat
                if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue;
                used[i] = true;
                list.add(nums[i]);
                recursion(nums, i + 1, list, ans, used);
                list.remove(list.size() - 1);
                used[i] = false;
            }
        }
    }

This problem is different from the previous one in that the given sequence repeats and there are several more places to deal with it.
1. Sort the given array first
2. Adding judgement sentences to eliminate repetition
3. Increase the storage of variables that have been used.
The next two or three points are explained in detail. The first point is that it is convenient to process duplicate data after sorting.
Point 2, if (i > 0 & & nums [i]== nums [i - 1] &! used[i - 1]) shows that since the first number can not be repeated, skipping the case of only one number, there is not much explanation in the middle. The latter is more difficult to understand, why the previous number is not used, choose to skip, because the rules in the loop first label the array, then recurse, and then backtrack. When the last number is marked as unused, it means that the loop is in the case of backtracking, which is equivalent to the case where there must have been a loop that handled the use before, and it is redundant to reprocess here. Another layer in the for loop is the judgment of! used[i], which is easy to see. In order to be concise, it can be merged with the whole if to become if (used[i] | I > 0 & & nums [i] == nums [i - 1] & used[i - 1]).

Three, lc40
Given an array of candidates and a target number, find out all combinations in candidates that can make numbers and targets.
Each number in candidates can only be used once in each combination.
Explain:
All numbers (including target numbers) are positive integers.
Unset cannot contain duplicate combinations.
Example 1:
Input: candidates = 10, 1, 2, 7, 6, 1, 5], target = 8,
The set of solutions is:
[ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6]]

public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> ans = new ArrayList<>();
        List<Integer> ansOne = new ArrayList<>();
        Arrays.sort(candidates);
        this.recursion(candidates, 0, target, ansOne, ans, new boolean[candidates.length]);
        return ans;
    }

    private void recursion(int[] nums, int start, int remained, List<Integer> list, List<List<Integer>> ans, boolean[] used) {
        if (remained == 0) {
            ans.add(new ArrayList<>(list));
        } else {
            for (int i = start; i < nums.length; i++) {
                // Judgment in a loop, reduce one recursion
                if (remained < nums[i]) {
                    break;
                }
                if (i > start && nums[i] == nums[i-1]) continue;
                list.add(nums[i]);
                recursion(nums, i+1,remained - nums[i], list, ans, used);
                list.remove(list.size()-1);
            }
        }
    }

Similarly, the target value is increased and decreases every time until 0. The process can be reduced to a negative number of shear processing. If (i > Start & nums [i]== nums [i-1]), start is a parameter of recursive function, which means that the value can be used in the first cycle.

Fourth, other topics can be used as templates.
For example, 77, 39, 46, 47, 216 and so on. Here's a comparison of the significance of different configuration parameters in the template.

// 1. Judging the sentences to be de-duplicated
// A When there is duplicate data in a given sequence, part of the result set cannot have the same value
if (used[i] || i > 0 && nums[i] == nums[i - 1] && !used[i - 1])
// b Given that there is duplicate data in a given sequence, there is no duplicate list data in the result set (the location is wrong, the value is the same)
 if (i > start && nums[i] == nums[i-1])

// 2. Scope of the cycle
a. int i = start   Executing from a given sequence down, no backoff is allowed
b. int i = 0    A given sequence of numbers can be chosen at will.

// 3. Recursive start parameter
a. Pass the current value to the recursive function i: The median of a sequence can be reused indefinitely
b. Transfer to recursive functions i+1: Each value can only be used once

Posted by baudday on Thu, 26 Sep 2019 01:41:46 -0700