zoukankan      html  css  js  c++  java
  • 回溯法

    1 解题要点

    回溯法是利用递归来求解便利一棵多叉树,其本质是深度优先搜索,通常求解的答案为树的所有叶子结点,但我们需要根据题目的要求边剪枝边遍历。

    1.1 何时用回溯法求解问题

    当题目中要求所有解,并且问题规模N不是很大,可以考虑使用回溯法。

    1.2 常规解题步骤

    permutation为例题,说明如何使用回溯法。

    1. 画递归树
      根据题目中所给数据,依次放入一个元素进入向量中,如果达到所求的叶子,则将整个向量存入结果中。
    2. 寻找剪枝条件
      一般剪枝条件具有具体的含义,需要仔细分析,或者专门写函数实现,常见条件如:
    1. 寻找求解结点
      大部分题目求解的是叶子结点,所以我们一般在dfs进入后,先判断是否达到叶子结点,若达到就保存后退出。但也有一些题目需要求解每个结点,如subsets,这时候需进入dfs后直接保存结果状态。

    2 解题模板

    void dfs(vector<vector<int>> &result, vector<int> &res, vector<int> &nums) {
        //如果是叶子,则存入结果
        if (res.size() == nums.size()) {
            result.push_back(res);
            return;
        }
    
        for (int i = 0; i < nums.size(); i++) {
    	    //剪枝条件,选择之前没有加入的元素
            if (find(res.begin(), res.end(), nums[i]) == res.end()) {
                res.push_back(nums[i]);
                dfs(result, res, nums);
                res.pop_back();  //回溯法的关键:回退
            }
        }
    }
    
    vector<vector<int>> permute(vector<int>& nums) {
        //设置保存结果的容器
        vector<vector<int>> result;
        vector<int> res;   //保存每个叶子结点
        //有时候需要把遍历到哪个元素的位置pos加入形参
        dfs(result, res, nums);   
        return result;
    }
    

    3 经典题目

    有的时候剪枝条件并不容易想,目前我遇到的题目剪枝方法包括:

    1. 已加入本次结果的元素不再加入
    if (find(res.begin(), res.end(), nums[i]) == res.end()) {
        xxx
    }
    
    1. 使用pos游标来取消之前元素的访问combination sum II,传参的时候需要加上游标pos,循环初始化时i = pos
    for (int i = pos; i < nums.size(); i++) {
    }
    
    1. 使用标志位来防止重复元素的访问,比如给一定一个向量(permutationsII)或字符串(string_permutations),要求所有排列。
      这时传参时需要带上标志位visited,是否引用都可以,不过引用可以减小传参开销
    void dfs(vector<int> nums, vector<bool> &visited, vector<int> &res, vector<vector<int>> &result);
    

    以下代码通过使用标志标志位来对重复元素进行访问

    for (int i = 0; i < nums.size(); i++) {
        // 通过以下判断来剪枝, nums此是已经被排序过了
        // case1: 当前位置元素访问过
        // case2: 当前位置元素与上个元素相同,如果上个元素没有访问过,那这个元素也没访问
        // 这样通过将强制规定相同元素的访问顺序,来排除了一些重复的解
        //注意,|| 的结合优先级小于&& !
        if (visited[i] || i != 0 && !visited[i-1] && nums[i] == nums[i-1]) {
            continue;
        }
        visited[i] = true;
        res.push_back(nums[i]);
        dfs(nums, visited, res, result);
        res.pop_back();
        visited[i] = false;
    }
    
  • 相关阅读:
    三个心态做人做学问 沧海
    成功走职场要找准自己的"快捷键" 沧海
    免费离线下载 拂晓风起
    Hibernate 获取某个表全部记录时 奇怪现象 (重复出现某个记录) 拂晓风起
    无法读取mdb 如果连接不了ACCESS mdb文件,就尝试安装MDAC 拂晓风起
    Netbeans 使用 Hibernate 逆向工程 生成hbm和pojo 拂晓风起
    如何点击单选框 radio 后面的文字,选中单选框 拂晓风起
    Java 连接access 使用access文件 不用配置 拂晓风起
    mysql下如何执行sql脚本 拂晓风起
    Hibernate配置access Hibernate 连接 access 拂晓风起
  • 原文地址:https://www.cnblogs.com/fariver/p/7224216.html
Copyright © 2011-2022 走看看