回溯法——combination-sum、combination-sum-ii

本文介绍了两种组合求和问题:CombinationSumI允许无限次选取集合中的元素;CombinationSumII则限制每个元素只能选取一次。通过递归深度优先搜索算法实现,并详细展示了如何去除重复结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题一  Combination Sum I,题目大意是这样的:有一个正整数集合C,和一个目标数T(T也为正整数)。现从C中选出一些数,使其累加和恰好等于T(C中的每个数都可以取若干次),求所有不同的取数方案。

要求:所得集合元素不能降序排列,结果不能有重复的集合。

       例如:C={2,3,6,7}  T=7

                  res={  [7],

                            [2, 2, 3] }

import java.util.*;
public class Solution {
    public ArrayList<ArrayList<Integer>> combinationSum(int[] num, int target) {
        ArrayList<ArrayList<Integer>> array=new ArrayList();
        if(num == null||num.length == 0){
            return array;
        }
        Arrays.sort(num);
        ArrayList<Integer> list=new ArrayList();
        dfs(num,0,0,target,list,array);
        return array;
    }
    public void dfs(int[] num,int index,int cur,int target,ArrayList<Integer> list,ArrayList<ArrayList<Integer>> array){
        if(cur == target){
            array.add(new ArrayList(list));
            return;
        }
        if(cur > target){
            return ;
        }
        for(int i=index;i<num.length;i++){
            
            if(i == index||num[i]!=num[i-1]){//若for(int j=index;j<i;j++)去重会超时
            list.add(num[i]);
            dfs(num,i,cur+num[i],target,list,array);
            list.remove(list.size()-1);
            }
        }
    }
}


 题二   Combination Sum II,与题一得区别是集合C中的每个数最多只能取一次,且结果集合中不能有重复的结果出现。

       例如:C={10,1,2,7,6,1,5}  T=8

                  res={ [1, 7]

                           [1, 2, 5]

                           [2, 6]

                           [1, 1, 6] }

相比于题一,去重是本题的关键!!!即在本层不选重复的元素。

import java.util.*;
public class Solution {
    public ArrayList<ArrayList<Integer>> combinationSum2(int[] num, int target) {
        ArrayList<ArrayList<Integer>> array=new ArrayList();
        if(num == null||num.length == 0)
            return array;
        
        Arrays.sort(num);
        ArrayList<Integer> list=new ArrayList();
        help(num,0,target,0,list,array);
        return array;
    }
    public void help(int[] num,int index,int target,int cur,ArrayList<Integer> list,ArrayList<ArrayList<Integer>> array)
        {
        if(cur == target)
            {
            array.add(new ArrayList(list));
            return;
        }
        if(cur > target)
            {
            return;
        }
        for(int i=index;i<num.length;i++)
            {
            list.add(num[i]);
            help(num,i+1,target,cur+num[i],list,array);
            list.remove(list.size()-1);
            //去重的方法,在本层不再选取重复的元素
            int j=i;
            for(;j+1<num.length&&num[j+1] == num[i];j++)
                {}
            i=j;
        }
    }
}



### 回溯法实验案例 回溯法是一种系统化的试探算法,它通过逐步构建解决方案并撤销不合适的部分来解决问题。以下是基于引用中的描述以及常见的实践方法设计的一个完整的回溯法实验案例。 #### 示例代码:求解 N 皇后问题 N 皇后问题是经典的回溯法应用之一,目标是在 \(n \times n\) 的棋盘上放置 \(n\) 个皇后,使得它们彼此之间不会互相攻击(即不在同一行、列或对角线上)。 ```python def solve_n_queens(n): def is_safe(row, col, queens): """判断当前位置是否安全""" for r, c in queens: if c == col or abs(r - row) == abs(c - col): # 检查列和对角线冲突 return False return True def backtrack(row, queens): """递归尝试每一行的可能摆放方案""" if row == n: # 如果已经摆放到最后一行,则找到一种解法 solutions.append(["." * c + "Q" + "." * (n - c - 1) for _, c in queens]) return for col in range(n): # 尝试每列的位置 if is_safe(row, col, queens): # 检查当前位置是否合法 queens.append((row, col)) # 放置皇后 backtrack(row + 1, queens) # 继续下一行 queens.pop() # 撤销选择 solutions = [] backtrack(0, []) return solutions if __name__ == "__main__": n = 4 # 设置棋盘大小 result = solve_n_queens(n) print(f"{n}皇后的解法如下:") for solution in result: for line in solution: print(line) print() ``` 此代码实现了 N 皇后问题的求解逻辑[^1]。其中 `is_safe` 函数用于检测某个位置是否可以放置皇后,而 `backtrack` 是核心函数,负责递归地探索所有可能性,并在发现不可行路径时及时返回。 --- #### 示例代码:组合总和问题 另一个典型的回溯法应用场景是 **组合总和** 问题,其目的是找出一组数中能够相加得到特定目标值的所有组合。 ```python def combination_sum(candidates, target): def backtrack(start, path, remaining): if remaining == 0: # 当剩余的目标值为零时,记录该组合 combinations.append(list(path)) return elif remaining < 0: # 超过目标值则停止继续搜索 return for i in range(start, len(candidates)): candidate = candidates[i] path.append(candidate) # 添加候选数值 backtrack(i, path, remaining - candidate) # 进入下一层次 path.pop() # 移除最后一个添加的数值 combinations = [] backtrack(0, [], target) return combinations if __name__ == "__main__": candidates = [2, 3, 6, 7] target = 7 result = combination_sum(candidates, target) print(f"组合总和的结果为 {result}") ``` 这段代码展示了如何利用回溯法解决组合总和问题[^2]。注意,在每次调用 `backtrack` 方法之前都会检查当前状态是否满足条件,从而减少不必要的计算开销。 --- #### 动态规划与回溯法的区别 虽然两者都涉及穷举所有的可能性,但动态规划通常会存储中间结果以避免重复计算,而回溯法则更倾向于即时丢弃不符合约束条件的部分解集。例如,在旅行商问题(TSP) 中采用动态规划的方式进行了优化处理[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值