Backtracking algorithm induction

Two ways of looking back

Look at different problem-solving methods and form different thinking.
Let me talk about the conclusion first. Backtracking problem-solving idea 1: It is to adopt two strategies of no choice and choice for each element that can be selected, and continue recursively. I recently watched the video of Huahuajiang and got idea 2: To complete the goal is divided into n stages, each stage will have different choices, and try different choices in each stage.
Let's take specific leetcode39 as an example to illustrate.

Title description

Input: an array of candidates without repeated elements, an int target.
Output: use the elements in candidates to form the target, and output how many combinations there are. The output result cannot be repeated.
Rule: The elements in candidates can be used multiple times.
For example, candidates=[2,3,6,7] target=7, return
[
[7],
[2, 2, 3]
]

Solve according to idea 1

The result set contains multiple solutions, represented by List<List> result. Each solution is a List list.
For each element in the candidate array candidates, we may or may not use it.
For example, for index = 0, we may or may not use candidates[0].
Do not use candidates[0], target unchanged, list unchanged, index+1, and enter the stage of selecting candidates[1].
Use candidates[0], the target becomes smaller, and add candidates[0] and index+1 to the list to enter the stage of selecting candidates[1]. Note that because the title says that an element can be reused, candidates[0] can use target/candidates[0] times at most.

Use path in the code to record how many times each element is selected. path[0]=candidates[0] number of selections.
Exit condition: When target=0, the problem is solved and the result set is added. When the subscript is out of range, or target=0, exit the loop.

	private List<List<Integer>> result = new ArrayList<List<Integer>>();
	private int[] path;
	private int[] candidates;
	/**
	 * path[i] = 使用nums[i]元素的次数
	 * @param candidates
	 * @param target
	 * @return
	 */
	public List<List<Integer>> combinationSumV2(int[] candidates, int target) {
    
    
		result.clear();
		path = new int[candidates.length];
		this.candidates = candidates;
		dfsV2(0, target);
		return result;
	}

	private void dfsV2(int idx, int target) {
    
    
		if (target == 0) {
    
    
			List<Integer> solution = new ArrayList<Integer>();
			for (int i = 0; i < path.length; i++) {
    
    
				for (int j = 1; j <= path[i]; j++) {
    
    
					solution.add(candidates[i]);
				}
			}
			result.add(solution);
			return;
		}
		if (idx >= candidates.length || target < 0) {
    
    
			return;
		}
		int maxCount = target / candidates[idx];
		for (int i = 0; i <= maxCount; i++) {
    
    
			path[idx] = i;
			dfsV2( idx + 1, target - i * candidates[idx]);
			path[idx] = 0;
		}
	}

Solve according to idea 2

Target=7, you can select any element of 2, 3, 6, 7, that is, any element in the candidates with subscripts from 0 to 4. Select 2, add 2 to the list, and enter the state target=5.
target=5, you can choose 2, 3. Because 6, 7 is greater than 5, there is no need to choose pruning. Select 2, add 2 to the list, and enter target=3.
target=3, you can choose 2,3. Because 6, 7 is greater than 3, there is no need to choose pruning. Choose 2, add 2 to the list, and enter target=1.

Exit condition: When target=0, the problem is solved and the result set is added.

Draw a recursive tree according to this idea, and find that there are duplicate solutions. It can be solved by limiting the starting point of the subscript. For example, after selecting 3 in the figure, the elements that can be selected are between 3, 6, and 7. Because 3, 6, and 7 are all greater than 2, there is no need to continue recursion. So the state on the recursive tree needs to be added with a starting subscript. Modify the recursion tree.

	public List<List<Integer>> combinationSumV3(int[] candidates, int target) {
    
    
		result.clear();
		Arrays.sort(candidates);
		this.candidates = candidates;
		if(candidates==null || candidates.length==0) return result;
		dfsV3(0,target,new ArrayList<Integer>());
		return result;
	}

	/**
	 * 在当前状态下,可以选择从start到数组结尾的任意一个元素到结果集
	 *
	 * @param start
	 * @param target
	 * @param list
	 */
	private void dfsV3(int start, int target,ArrayList<Integer> list) {
    
    
		if(target == 0) {
    
    
			//注意结果需要完全拷贝
			result.add(new ArrayList<Integer>(list));
			return;
		}
		if(start>= candidates.length) return;
		for(int i = start;i<candidates.length;i++){
    
    
			if(candidates[i] > target) break;
			list.add(candidates[i]);
			dfsV3(i,target-candidates[i],list);
			list.remove(list.size() - 1);
		}

	}

Each time dfs is a height of the tree.

Guess you like

Origin blog.csdn.net/flying_all/article/details/105504060