这是一道典型的回溯法的子集数问题。(因为可以重复,类似与图的m色问题)
每一条路径可以选择的数是candidates的每一个元素。
INPUT:
[2,3,6,7]
[7]
x= [0,0,1,-1]表示解为[2,2,3]
Solution 1:回溯法1
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
int t=0;
int sum=0;
//先排序,这样方便后面去重
Arrays.sort(candidates);
//每一个可行解的长度小于等于target/candidates[0]
//+1是因为 解是在路径上,t只是层数,层数比解个数要多一个。比如4/1=4 maxResLen=5。
//t=0,1,2,3 ,4,得1+1+1+1,此时sum=4<=target,要在进入下一层t=5,此时满足t==maxResLen,才return
int maxResLen=target/candidates[0]+1;
//x保存解的下标
int []x=new int[maxResLen];
//解初始化为-1
for(int i=0;i<maxResLen;i++)
x[i]=-1;
List<List<Integer>> res=new ArrayList<>();
backtrack(t,candidates,target,sum,x,maxResLen,res);
return res;
}
private void backtrack(int t,int[] candidates, int target,int sum,int []x,int maxResLen,List<List<Integer>> res){
if(t==maxResLen)return;
if(sum==target){
List<Integer> oneRes=new ArrayList<Integer>();
for(int i=0;i<maxResLen;i++){
if(x[i]!=-1){
oneRes.add(candidates[x[i]]);
}
}
//去重。因为前面已经排了序,所以只有非递减的解才是不重复的
int flag=1;
for(int i=1;i<oneRes.size();i++){
if((int)oneRes.get(i)<(int)oneRes.get(i-1))flag=0;
}
if(flag==1)res.add(oneRes);
return;
}
for(int i=0;i<candidates.length;i++){
//试探性先选取这个数
//sum先加上
x[t]=i;
sum+=candidates[i];
if(sum<=target){
//如果满足条件,则进入下一层
backtrack(t+1,candidates,target,sum,x,maxResLen,res);
//准备回溯
x[t]=-1;
sum-=candidates[i];
}
else{ //不满足条件,就还原
x[t]=-1;
sum-=candidates[i];
}
}
}
}
Solution 2:回溯法2
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
int t=0;
int sum=0;
Arrays.sort(candidates);
int maxResLen=target/candidates[0]+1;
int []x=new int[maxResLen];
for(int i=0;i<maxResLen;i++)
x[i]=-1;
List<List<Integer>> res=new ArrayList<>();
backtrack(t,candidates,target,sum,x,maxResLen,res);
return res;
}
private boolean backtrack(int t,int[] candidates, int target,int sum,int []x,int maxResLen,List<List<Integer>> res){
if(t==maxResLen-1)return false;
for(int i=0;i<candidates.length;i++){//指 2 3 6 7
if(sum+candidates[i]<=target){
x[t]=i;//t是位置
sum+=candidates[i];
if(sum==target){
List<Integer> oneRes=new ArrayList<Integer>();
for(int j=0;j<=t;j++){
if(x[j]!=-1){
oneRes.add(candidates[x[j]]);
}
}
int flag=1;
for(int j=1;j<oneRes.size();j++){
if((int)oneRes.get(j)<(int)oneRes.get(j-1))flag=0;
}
if(flag==1)res.add(oneRes);
//这一层直接找到的话,就不用再进下一层了,后续不用继续执行。
continue;
}
if(backtrack(t+1,candidates,target,sum,x,maxResLen,res)) return true;
else{
x[t]=-1;
sum-=candidates[i];
}
}
}
return false;
}
}
Solution 1:回溯法3
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
int t=0;
int sum=0;
Arrays.sort(candidates);
int maxResLen=target/candidates[0]+1;
int []x=new int[maxResLen];
for(int i=0;i<maxResLen;i++)
x[i]=-1;
List<List<Integer>> res=new ArrayList<>();
backtrack(t,candidates,target,sum,x,maxResLen,res);
return res;
}
private void backtrack(int t,int[] candidates, int target,int sum,int []x,int maxResLen,List<List<Integer>> res){
if(t==maxResLen-1)return;
for(int i=0;i<candidates.length;i++){//指 2 3 6 7
if(sum+candidates[i]<=target){
x[t]=i;//t是位置
sum+=candidates[x[t]];
if(sum==target){
List<Integer> oneRes=new ArrayList<Integer>();
for(int j=0;j<=t;j++){
if(x[j]!=-1){
oneRes.add(candidates[x[j]]);
}
}
int flag=1;
for(int j=1;j<oneRes.size();j++){
if((int)oneRes.get(j)<(int)oneRes.get(j-1))flag=0;
}
if(flag==1)res.add(oneRes);
continue;
}
backtrack(t+1,candidates,target,sum,x,maxResLen,res);
x[t]=-1;
sum-=candidates[i];
}
}
return ;
}
}