【算法】剪枝&回溯

剪枝&回溯

剑12 矩阵中的路径@@

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。

[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]

但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。

示例 1:

输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
示例 2:

输入:board = [[“a”,“b”],[“c”,“d”]], word = “abcd”
输出:false

来源:力扣(LeetCode)

分析:经典回溯,但此题只要求返回true or false,能返回所有路径吗?

friend ostream &operator<<(ostream &out, const vector<vector<char>> &board)
{
   
   
//重载 << ,用于调试输出二维数组
    for (auto i : board)
    {
   
   
        for (auto j : i)
        {
   
   
            out << j << " ";
        }
        out << endl;
    }
    out << endl;
    return out;
}
bool backtrack(vector<vector<char>> &board, int row, int col,
               const string &word, int idx)
{
   
   
    //正确返回终止条件,找到一个正确分支即终止
    if (idx == word.size())
        return true;
    //数组越界终止条件
    if (row < 0 || row >= board.size() ||
        col < 0 || col >= board[0].size())
        return false;
    //如果表格当前字符不等于欲匹配字符,剪掉此分支
    //这样还在增长的分支就是可能正确的分支
    if (word[idx] != board[row][col])
        return false;
    //设置标记位,如果后续回到此位置,比对字符即知
    board[row][col] = '*';
    //往四个方向走,有一个方向返回true,程序返回true
    if (backtrack(board, row - 1, col, word, idx + 1) ||
        backtrack(board, row + 1, col, word, idx + 1) ||
        backtrack(board, row, col - 1, word, idx + 1) ||
        backtrack(board, row, col + 1, word, idx + 1))
        return true;
    //回溯。走不通时该位置还原为本来的字母
    board[row][col] = word[idx];
    //cout<<board;
    //没有分支返回true。程序返回false
    return false;
}
bool exist(vector<vector<char>> &board, string word)
{
   
   
    if (board.empty() || board[0].empty())
        return word.empty();
    for (int row = 0; row < board.size(); ++row)
    {
   
   
        for (int col = 0; col < board[0].size(); ++col)
        {
   
   
        //暴力搜索每一点作为起点
            if (backtrack(board, row, col, word, 0))
                return true;
        }
    }
    return false;
};

剑13.机器人的运动范围

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

示例 1:

输入:m = 2, n = 3, k = 1
输出:3
示例 1:

输入:m = 3, n = 1, k = 0
输出:1

第一版代码

根据上面一题直接写出此题的代码,提交结果为超时,本地运行超时用例时等待很久,说明有死循环。本着“不放弃每一段代码”的精神,我决定debug后再看书上的答案
在这里插入图片描述

class Solution {
   
   
public:
    int K;
    friend ostream &operator<<(ostream &out, const vector<vector<char</
&lt;think&gt;好的,我现在需要帮助用户理解回溯算法中的剪枝模型及其工作原理。首先,用户的问题明确,但作为新手,可能需要从基础开始解释。回溯算法本身是一种通过试错来寻找所有可能解的算法,但当搜索路径明显不可能得到解时,剪枝就显得尤为重要。 首先,我应该回顾回溯算法的基本概念,确保用户有共同的理解基础。回溯通常通过递归实现,遍历所有可能的候选解,并在确认某候选解不可能成为有效解时,提前终止对该路径的探索,这就是剪枝剪枝的目的是减少不必要的计算,提高效率。 接下来,需要详细说明剪枝模型的定义。剪枝模型指的是在回溯过程中识别并跳过无效分支的策略。这里要强调剪枝的条件,比如约束条件和限界条件。约束条件确保当前路径满足问题的基本要求,而限界条件则用于优化问题,比如在求最短路径时提前放弃比已知解更长的路径。 然后,实现方式是关键。用户可能想知道具体如何操作。例如,在排列问题中,如果当前元素已经被使用过,就可以剪枝。或者在组合总和问题中,如果当前和超过目标值,提前返回。这部分需要结合具体例子,比如全排列或子集生成,来说明剪枝是如何应用的。 同时,用户提供的引用材料中提到了一些例子,比如全排列问题中的used数组,或者二进制枚举的方法。需要将这些例子融入解释中,增强权威性。例如,引用[3]提到递归法方便剪枝,而二进制枚举适用于小n的情况,这可能帮助用户理解不同场景下的剪枝应用。 还要注意区分剪枝的类型,比如可行性剪枝和最优性剪枝。可行性剪枝处理的是不可能满足条件的情况,而最优性剪枝则是在寻找最优解时,舍弃不可能更优的路径。动态规划和分支限界算法中的剪枝策略可能有所不同,但回溯中主要关注可行性。 用户可能存在的疑问包括:如何确定剪枝条件?会不会因为剪枝而遗漏有效解?这时候需要解释剪枝的条件必须准确,通常基于问题本身的约束,通过数学推导或逻辑判断来确保正确性。例如,在N皇后问题中,剪枝条件是通过检查当前皇后的位置是否与之前的冲突,这不会遗漏解,因为冲突的位置无论如何都无法构成有效解。 此外,可能需要提到剪枝的实现技巧,比如预处理排序,提前终止递归等。例如,在组合总和中,先对数组排序,可以在遇到超过目标值的元素时立即停止搜索,避免不必要的递归。 最后,总结剪枝的重要性,并引导用户思考相关问题,比如如何设计剪枝条件,或者在不同问题中如何调整剪枝策略。这样不仅回答了用户的问题,还帮助他们扩展思路,应用到实际问题中。&lt;/think&gt;### 回溯算法中的剪枝模型及其工作原理 #### 1. 剪枝模型的定义 剪枝回溯算法的核心优化策略,其核心思想是**在递归搜索过程中提前终止无效路径的探索**。通过分析问题特性,设计约束条件或启发式规则,跳过不可能产生有效解的分支,从而减少搜索空间[^1][^3]。 #### 2. 剪枝的实现方式 剪枝模型可分为两种类型: - **可行性剪枝**:当当前路径明显违反问题的约束条件时终止搜索 例如在$N$皇后问题中,若当前放置的皇后与已有皇后冲突,立即回溯: ```python def is_valid(board, row, col): for i in range(row): # 检查列和对角线冲突 if board[i] == col or abs(row-i) == abs(col-board[i]): return False return True ``` - **最优性剪枝**:当当前路径无法产生比已知最优解更好的结果时终止搜索 例如在旅行商问题中,若当前路径长度已超过已知最短路径,立即回溯: ```python if current_cost &gt; best_cost: # 当前路径已不可能是最优解 return ``` #### 3. 剪枝条件的设计原则 | 条件类型 | 设计依据 | 应用示例 | |---------|----------|---------| | 显式约束 | 问题本身的限制条件 | 全排列中元素不可重复使用[^3] | | 隐式约束 | 问题解的数学特性 | 组合总和问题中元素排序后提前终止[^2] | | 对称性剪枝 | 避免重复计算对称解 | 子集问题中跳过相同元素的分支[^3] | #### 4. 典型实现模式 ```python def backtrack(path, choices): if 满足终止条件: 记录解 return for choice in choices: if 不满足剪枝条件: # &larr; 关键剪枝点 做选择 backtrack(更新后的path, 更新后的choices) 撤销选择 ``` #### 5. 剪枝效果分析 对于规模为$n$的问题,未剪枝的复杂度常为$O(n!)$或$O(2^n)$,剪枝后复杂度可降为: $$T(n) = O(k^n) \quad (k &lt; 2)$$ 例如八皇后问题剪枝后复杂度从$O(n!)$降为$O(n!/(n-k)!)$[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值