【算法】剪枝&回溯

剪枝&回溯

剑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:

扫描二维码关注公众号,回复: 12251891 查看本文章

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

第一版代码

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

class Solution {
    
    
public:
    int K;
    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 cant_move(int row, int col, int k)
    {
    
    
        int cnt = 0;
        while (row or col)
        {
    
    
            cnt += row % 10 + col % 10;
            row /= 10;
            col /= 10;
        }
        return cnt > k;
    }
    
    struct arrayHash{
    
    
    //避免哈希冲突
        int operator()(const array<int, 2> &p) const {
    
     return p[0] * 1e2 + p[1]; }
    };
    unordered_set<array<int, 2>,arrayHash> um;
    void backtrack(vector<vector<char>> &board, int row, int col)
    {
    
    
        //以下3个条件进行剪枝
        //1、数组越界终止条件
        if (row < 0 || row >= board.size() ||
            col < 0 || col >= board[0].size())
            return;
        //2、标志位,防止重复遍历
        if ('o' != board[row][col])
            return;
        //3、检测该点是否符合题目条件
        if (cant_move(row, col, K))
            return;
        //设置标记位,如果后续回到此位置,比对字符即知
        board[row][col] = '*';
        //cout << board;
        array<int, 2> p;
        p[0] = row, p[1] = col;
        if(um.find(p)==um.end()){
    
    
            um.insert(p); //放入哈希表
        }
        
        //往四个方向走,
        backtrack(board, row - 1, col);
        backtrack(board, row + 1, col);
        backtrack(board, row, col - 1);
        backtrack(board, row, col + 1);
        //回溯。走不通时该位置还原为本来的字母
        board[row][col] = 'o';
        //cout << board;
        //没有分支返回true。程序返回false
        return;
    }
    int movingCount(int m, int n, int k) {
    
    
        vector<vector<char>> b(m,vector<char>(n,'o'));
        K=k;
        backtrack(b, 0, 0);
        return um.size();
    }
    
};

22. 括号生成

给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:

[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]
分析:纯剪枝,无回溯

class Solution {
    
    
    vector<string> ans;
    int N;
public:
    vector<string> generateParenthesis(int n) {
    
    
        N = n;
        DFS("",0,0);
        return ans;
    }
    void DFS(string s,int left,int right){
    
    
    	//两个剪枝条件
        if(left < right) return;
        if(left > N) return;
        //终止递归条件
        if(s.length() == 2*N){
    
    
            ans.push_back(s);
            return;
        }
        //分支
        DFS(s+"(",left+1,right);
        DFS(s+")",left,right+1);
    }
};

51.N皇后

copy了份代码,加点注释分析下

class Solution {
    
    
public:
    vector<vector<string>> ans;
    vector<vector<string>> solveNQueens(int n) {
    
    
        //这里一定要写n,给record初始化
    	vector<int> record(n);
    	//字符串s初始化为n个点
        string s="";
        for(int i=0;i<n;i++){
    
    
            s+='.';
        }
        //棋盘初始化为n个向量,每个都是n个点,即n*n棋盘
        vector<string> temp(n, s);
        helper(0, n, temp, record);
        return ans;
    }
    /*
    l表示行数,n为题目中的n,temp为棋盘,recode记录?
    */
    void helper(int l, int n, vector<string>&temp, vector<int>& record){
    
    
    	//正常终止递归条件,即棋盘填满了
        if(l==n){
    
    
            ans.push_back(temp);
            return;
        }
        //从左到右依次填入棋子
        for(int i=0;i<n;i++){
    
    
        	//从左到右依次填入皇后
            record[l]=i;
            //如果不能填入,什么也不做,相当于剪枝
            //如果能填入皇后,则填入
            if(isok(record, l)){
    
    
                temp[l][i]='Q';
                //填入本行的皇后后,递归寻找下一行皇后的位置
                helper(l+1, n, temp, record);
                //回溯,即取消在该点填入皇后
                temp[l][i]='.';
            }
        }
    }
    //检查皇后能否填入的算法,row是递归程序当前的行
    bool isok(vector<int>& record, int row){
    
    
        for(int i=0;i<row;i++){
    
    
            if(record[i]==record[row]||row-record[row]==i-record[i]||row+record[row]==i+record[i])return false;
        }
        return true;
    }
};

以n = 4不剪枝为例,每次递归程序处理一行。例如(0,0)填入后,又有(1,0)、(1,1)、(1,2)、(1,3)四种填法,第三行又有4种填法。关键是填入皇后(1,0)后,递归到下一层,递归返回后又重新填入(1,1),实现了每一层的4个分支

以下是我的解法:

#include <bits/stdc++.h>
using namespace std;
int cnt = 0;
void outPut(const vector<vector<char>> &b)
{
    
    
    for (int i = 0; i < b.size(); i++)
    {
    
    
        for (int j = 0; j < b.size(); j++)
        {
    
    
            cout << b[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}
void mark(int x, int y, vector<vector<char>> &b)
{
    
    
    int n = b.size();
    for (int i = 0; i < n; i++)
    {
    
    
        for (int j = 0; j < n; j++)
        {
    
    
            b[x][j] = '+';
            b[i][y] = '+';
            if (i + j == x + y or i - j == x - y)
            {
    
    
                b[i][j] = '+';
            }
        }
    }
    b[x][y] = 'x';
    //outPut(b);
}
void dfs(int level, vector<vector<char>> &b)
{
    
    
    if (level == b.size())
    {
    
    
        outPut(b);
        cnt++;
        return;
    }
    for (int i = 0; i < b.size(); i++)
    {
    
    
        if (b[level][i] == '.')
        {
    
    
            vector<vector<char>> tmp = b;
            mark(level, i, b);
            dfs(level + 1, b);
            b = tmp;
            //outPut(b);
        }
    }
}
int main()
{
    
    
    int N;
    cin >> N;
    vector<vector<char>> b(N, vector<char>(N, '.')); //棋盘
    dfs(0, b);
    //cout << cnt << endl;
    system("pause");
}

36.有效数独

在这里插入图片描述

class Solution
{
    
    
    set<char> st1;
    set<char> st2;
public:
    bool cheak(char c, set<char> &st)
    {
    
    
        if (c <= 57 && c >= 49)
        {
    
    
            if (st.find(c) != st.end())
                return 0;
            else
                st.insert(c);
        }
        return 1;
    }
    bool isValidSudoku(vector<vector<char>> &board)
    {
    
    
        for (int i = 0; i < 9; i++)
        {
    
    
            st1.clear();
            st2.clear();
            for (int j = 0; j < 9; j++)
            {
    
    
                if (!cheak(board[i][j], st1))
                    return 0;
                if (!cheak(board[j][i], st2))
                    return 0;
            }
        }
        for (int u = 0; u < 3; u++)
        {
    
    
            for (int v = 0; v < 3; v++)
            {
    
    
                st1.clear();
                for (int i = 3 * u; i < 3 * u + 3; i++)
                {
    
    
                    for (int j = 3 * v; j < 3 * v + 3; j++)
                    {
    
    
                        if (!cheak(board[i][j], st1))
                            return 0;
                    }
                }
            }
        }
        return 1;
    }
};

37.解数独

class Solution {
    
    
public:
    bool solveSudoku(vector<vector<char>>& board) {
    
    
        for ( int i = 0 ;i < board.size();i++){
    
    
            for(int j=0;j<board[i].size();j++){
    
    
                if(board[i][j]=='.'){
    
    //开始填入
                    for(char c='1';c<='9';c++){
    
    
                        if(isValid(board,i,j,c)){
    
    
                            board[i][j]=c;//填入
                            if(solveSudoku(board)) return true;//解完
                            else board[i][j]='.';//下层返回错,回溯
                        }
                    }
                    return false;//填完了1-9 都不行 就返回错
                }
            }
        }
      return true;
    }
private:
    bool isValid(vector<vector<char>>& board,int row,int col,char c){
    
    
      for(int i=0;i<9;i++){
    
    
        if(board[i][col]==c) return false;
        if(board[row][i]==c) return false;
        if(board[3*(row/3)+i/3][3*(col/3)+i%3]==c) return false;
      }
      return true;
    }
};

猜你喜欢

转载自blog.csdn.net/Protocols7/article/details/104540694