Backtracking algorithm problem solving ideas
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.