代码随想录训练营Day22:回溯算法解决 重新安排行程、N皇后、解数独

这次的三个题目都是属于比较难的题目,也比较难想到相应的解决方法,需要经常复习。

1.332重新安排行程

本题是一个组合问题的一个模板,需要返回的是一个叶子节点对应的结果。

本题的难点:

  1. 设置对应的一个哈希表:unordered_map<string,map<string,int>> targes; //出发的机场,到达的机场,航班次数,用这样的方式来存储映射关系,if(target.second>0)通过判断航班次数来确定是否可以使用。
  2. 返回值为bool,因为本题只需要返回一个即可,即找到一个满足的条件,类似于排列中找到一个满足的叶子节点所以在里面如果backtracking返回值为true的时候即结束。
  3. for(pair<const string,int>& target:targes[result[result.size()-1]]):这个for循环的作用是遍历出发的机场,每一次backtracking都会更新result,从而让for循环里面的targets也发生变化。以下图为例,根据对应的映射关系来进行遍历,在最终达到长度(即所有票全部用完)的时候返回。
  4. for(const vector<string>&vec:tickets){targes[vec[0]][vec[1]]++;//将对应的起飞到达的机票全部遍历进去}:用这个先进行初始化,保证后面的执行。

class Solution {
public:
    //出发的机场,到达的机场,航班次数
    unordered_map<string,map<string,int>> targes; 
    //因为只需要找一个即可,所以返回的是一个bool值
    bool backtracking(int tickNum,vector<string>& result){
        if(result.size() == tickNum+1){
            return true;//代表找到了一个
        }
        for(pair<const string,int>& target:targes[result[result.size()-1]]){
            //这里实际上遍历的是对应的一个出发的机场
            //first对应的是一个到达的机场
            //second对应的是一个航班的次数
            if(target.second>0){
                result.push_back(target.first);//代表找到了一个新的起飞地
                target.second--;
                if(backtracking(tickNum,result)) return true;//剪枝操作,只需要找到一个
                result.pop_back();
                target.second++;
            }
        }
        return false;
    }
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        targes.clear();
        vector<string> result;
        for(const vector<string>&vec:tickets){
            targes[vec[0]][vec[1]]++;//将对应的起飞到达的机票全部遍历进去
        }
        result.push_back("JFK");//起飞地
        backtracking(tickets.size(),result);
        return result;
    }
};

2.51N皇后

本题是一个组合问题的升级版,所以可以先按照组合问题的方式做出大致的框架。

本题的难点:

  1. 判断当前节点是否可以存放‘Q'即isValid函数:通过判断当前列,45°,135°角上是否存在'Q'来确定。为什么不用判断行呢?因为每次backtracking的时候我们都会更新深度即row,所以每行肯定不会存在'Q'
  2. std::vector<std::string> chessbox(n,std::string(n,'.'));:初始化的一点小技巧
class Solution {
public:
    bool isValid(int row,int col,vector<string>& chessbox){
        for(int i = 0;i<row;i++){
            if(chessbox[i][col] == 'Q'){//列是否满足
                return false;
            }
            for(int i = row,j = col;i>=0&&j>=0;i--,j--){//45°是否满足
                if(chessbox[i][j] == 'Q'){
                    return false;
                }
            }
            //135°是否满足
            for(int  i = row,j = col;i>=0&&j<chessbox.size();i--,j++){
                if(chessbox[i][j] == 'Q'){
                    return false;
                }
            }
        }
        return true;
    }
    //组合问题
    void backtracing(vector<vector<string>>& result,vector<string>& chessbox,int depth){
        if(depth == chessbox.size()){
            result.push_back(chessbox);
            return;
        }
        for(int j = 0;j<chessbox.size();j++){
            if(isValid(depth,j,chessbox)){
                chessbox[depth][j] = 'Q';
                backtracing(result,chessbox,depth+1);
                chessbox[depth][j] = '.';
            }
        }

    }
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>> result;
        std::vector<std::string> chessbox(n,std::string(n,'.'));
        backtracing(result,chessbox,0);
        return result;
    }
};

3.37解数独

本题也是属于组合问题里面的一种,但是由于我们只需要返回数独的一个结果即可,所以叶子节点中只有一个结果,我们使用bool值作为返回参数,进行剪枝操作。

本题的难点:

  1. 判断是否有效:1.判断行和列是否存在该值。2.对这个块内判断是否存在该值,此时我们使用((3*x+a)/3)*3=3*x来确定这个小3*3数组的左上角的位置,遍历小的数组来判断是否有效。
  2. 剪枝操作:双重for循环里面遍历所有的位置,if(board[i][j]!='.')continue;//代表此时这个点有数,不需要进行操作了。
  3. return false;//对应的位置,代表遍历了1-9都没有值满足,此时即返回false
  4. if(backtracking(board)) return true;//如果全部找到了,返回true,只需要找到一个满足条件的即可,不像组合问题里面将循环终止条件放到最前面,因为这个里面的循环终止条件是所有的位置全部遍历完才可以返回true。
class Solution {
public:
    bool isValid(int i,int j,char k,vector<vector<char>>& board){
        for(int row = 0;row<board.size();row++){//判断行
            if(board[row][j] == k){
                return false;
            }
        }
        for(int col=0;col<board[0].size();col++){
            if(board[i][col] == k){
                return false;
            }
        }
        int startrow = (i/3)*3;
        int startcol = (j/3)*3;
        for(int row = startrow;row<startrow+3;row++){
            for(int col = startcol;col<startcol+3;col++){
                if(board[row][col]==k){
                    return false;
                }
            }
        }
        return true;
    }
    bool backtracking(vector<vector<char>>& board){
        for(int i = 0;i<board.size();i++){
            for(int j = 0;j<board[0].size();j++){
                if(board[i][j]!='.')continue;//代表有数
                for(char k = '1';k<='9';k++){
                    if(isValid(i,j,k,board)){
                        board[i][j] = k;
                        if(backtracking(board)) return true;//如果全部找到了,返回true
                        board[i][j] = '.';
                    }
                }
                return false;//代表这个地方什么都填不了,返回false;
            }
        }
        return true;
    }
    void solveSudoku(vector<vector<char>>& board) {
        backtracking(board);
    }
};

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值