- 用深信服的一个问题来引出此题:
题目描述: 给定一堆题目所对应的分值,请你从中挑选一些题目,使其分值正好是一百分
解题思路:
- 将分值一个个加到一个列表中,一旦找到就输出列表
- 否则将所有元素加入列表后,将其回溯,加入下一个元素
- 可以在加入元素大于所要的100以后,就回溯
import java.util.List;
import java.util.ArrayList;
/*给定一堆题目所对应的分值,请你从中挑选一些题目,使其分值正好是一百分
* coded by Jerome
*/
public class demo1 {
public static List<Integer> list = new ArrayList<>();
public static List<Integer> backtrack(int[] a,int target,int i){
while(i<a.length){
list.add(a[i]);
if(sum(list) == target)
System.out.println(list);
i++;
backtrack(a, target, i);
list.remove(list.size()-1);
}
return list;
}
public static int sum(List<Integer> list){
int sum = 0;
for(int x:list)
sum += x;
return sum;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] a = {10,20,80,70};
int target = 100;
backtrack(a,target,0);
//System.out.println(list);
}
}
-
leetcode上的这题和上面这题的区别,其实就在于对于每个数字可以重复选择
- 同时对于回溯的条件作了优化,一旦超过给定值,就回溯
题目描述:给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。
说明:
- 所有数字(包括 target)都是正整数。
- 解集不能包含重复的组合。
解题思路:
- 从array 里找到所有的组合,它的和是等于target的。任何组合只要是它的和等于target的话,都需要找到,但是不需要重复的。这道题中是可以重复利用一个数字的,那么我们就需要每次都代入同一个数字,直到它之和达到target 或者它超过了target, 然后在倒退回去一个数字,继续找下一个数字,这种情况肯定是要用递归了。这里是backtracking,每次倒退回一个数字,需要继续递归下去,在倒退,一直重复直到搜索了所有的可能性。
- 举个例子分析
[2,3,6,7] target 7
2 选2,sum = 2
2+2 选2,sum = 4
2+2+2 选2,sum = 6
2+2+2+2 选2,sum = 8 这时候 sum已经超出target,需要返回到上一个数字
2+2+2+3 选3,sum = 9, 也超出了target, 这里我们可以发现,如果是sorted array的话,从小到大,只要一 次超出,后面的数字必然也会超出target,所以可以在第一次超出的时候就直接跳出这一个迭代
2+2+3 选3,sum = 7,等于target, 此时返回并且跳出这一个迭代,因为后面的数字肯定超出(array里不 会有重复的数字)
2+3 选3,sum = 5,小于target,继续递归同一个数字
2+3+3 选3,sum = 8,超出target,返回上一个数字
2+6 选6,sum = 8,超出target,返回上一个数字
3 选3,这里继续从3开始递归
...
...
...
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(candidates);
backtrack(list,new ArrayList<>(),candidates,target,0);
return list;
}
public static boolean backtrack(List<List<Integer>> list,List<Integer> temp,int[] a,int remain,int start){
if(remain < 0)
return false;
if(remain == 0){
list.add(new ArrayList<Integer>(temp));
return false;
}
else{
for(int i = start;i<a.length;i++){
boolean flag = true;
temp.add(a[i]);
flag = backtrack(list, temp, a, remain-a[i], i);
temp.remove(temp.size()-1);// not i+1,because we can use same number
if(!flag)
break;
}
return true;
}
}
}