代码随想录算法训练营第30天 | 回溯算法part06都是难题● 332.重新安排行程 ● 51. N皇后 ● 37. 解数独 ● 总结

都是难题,一刷建议看看思路,跳过那种。但我还是想做一下,迟早要学的。

#332 重新安排行程 Hard!!

做了我整整1hr,用各种multimap,set,不停地打补丁,放弃了。分析原因是,思路好像出来的很快,能想清,但痛点在于找不到合适的数据结构来装(如何记录映射关系):记录from to,还要记录是否用过,还要按lexical顺序

这题其实更像图论,算是“深搜中使用了回溯” 

看了一遍随想录解析,我发现我对这道题干复杂的题,审题和理解上有很多问题,这也是我做不出来的原因:

1. 是否全部用上我一开始以为可以有那种短的,其实不是:

2. 是边找/构成边选lexical最小的吗:对的

其他重要思路:

1.把from to这样放到map里,因为map不会按value排列,所以想要同一个from的,按to的顺序排列,就需要把同一个from的to都整合在一起(用set,或map,结合后面的理由,选择用map)。然后我们既然已经这么复杂了,就不要另外弄个叫做used的set那样了,就都放在一起算了

 看了随想录的思路自己又写了一遍,终于过了:但是有个关键点我一开始没考虑到,又看了随想录的代码才注意到:call backtrack后面加了一行:if(path.size() == ticket_num + 1) return;

这里我其实还稍微有点不理解,但我今天先不想想了

    vector<string> path;
    unordered_map<string,map<string,int>> map;//key structure
    
    void backtrack(int ticket_num){
        //if(ticket_num+1==path.size()) return;
        for(auto &pair:map[path.back()]){  //map[]->map second
            //go through the ordered tos of new from
            if(pair.second>0){
                path.push_back(pair.first);
                pair.second--;
                backtrack(ticket_num);
                if(path.size() == ticket_num + 1) return;
                path.pop_back();
                pair.second++;
            }
        }
    }

    vector<string> findItinerary(vector<vector<string>>& tickets) {
        for(auto &flight:tickets){
            map[flight[0]][flight[1]]++;
        }
        path.push_back("JFK");
        backtrack(tickets.size());
        return path;
    } 

这些难题每天做一个和别的难度没这么大的搭配在一起比较好。还有不要一大早刚开始学习就做这么难的题,影响状态。剩下俩明后再说,先学贪心了

#51 n皇后 Hard 回溯棋盘类的典型

直接看了随想录思路学的:

棋盘类回溯:for loop长度=棋盘宽度,递归深度=棋盘高度

皇后们的约束条件:不能同行 && 不能同列 && 不能同斜线 (45,135)

棋盘从上往下一行行逐个放皇后(row++)每放一个新的就检查和原来的会不会有碰撞 

check valid有两个要注意的地方:1 同一行逐个放所以不用查同行的 2 45度和135度其实有四个方向,但是我们只用检查当前上方的,也就是只用查左上和右上

其实backtrack思路很简单,可能加上isvalid稍微复杂点:

下面是学了随想录自己写的:实现中写错了一次,就是isvalid第一步是检查同列(行不停变化),但是我写成检查同行了,这里易错

    vector<vector<string>> res;
    

    bool isValid(int row, int col, int n, vector<string> &puzzle){
        //check col
        for(int i=0;i<n;i++){
            if(puzzle[i][col]=='Q') return false;
        }

        for(int i=row, j= col;i>=0 && j>=0; i--,j--){
            if(puzzle[i][j]=='Q') return false;
        }
        for(int i=row, j= col;i>=0 && j<n; i--,j++){
            if(puzzle[i][j]=='Q') return false;
        }
        return true;
    }

    void backtrack(int row, int n, vector<string> &puzzle){

        if(row==n){
            res.push_back(puzzle);
            return;
        }
        
        for(int i=0;i<n;i++){
            if(isValid(row, i, n,puzzle)){
                puzzle[row][i]='Q';
                backtrack(row+1,n,puzzle);
                puzzle[row][i]='.';
            }
        }

    }

    vector<vector<string>> solveNQueens(int n) {
        res.clear();
        vector<string> puzzle(n,string(n,'.'));
        backtrack(0,n,puzzle);
        return res;
    }

#37解数独

看了随想录的思路其实很直接,很直白,好理解。注意不需要找结束条件,他所有数找完会自己结束的,backtrack func结束前总会return一个bool的。

自己看了思路再实现的时候出了以下几个错误:

先是backtrack里忘了加这个条件if(board[i][j]=='.' ),然后加了之后又加反了,是需要是dot 是空的,我们才操作。

然后我觉得backtrack的逻辑,和返回,和以往别的回溯不太一样,易错

if(backtrack(board))如果是true就直接返回true结束本函数了,不用再做 board[i][j]='.';的复位操作了,因为这道题目标是填满数独的表。只有找不到的时候才需要复位,然后我们去找别的路径。

if(backtrack(board)) res=true; 这个是我一开始的错误写法,因为我总觉得还要执行归位复位,错的!!

bool isValid(int row,int col,char k,vector<vector<char>>& board){
        for(int i=0;i<board.size();i++){
            if(board[i][col]==k) return false;
        }
        for(int i=0;i<board[0].size();i++){
            if(board[row][i]==k) return false;
        }

        int startrow=(row/3)*3;
        int startcol=(col/3)*3;
        for(int i=startrow;i<startrow+3;i++){
            for(int j=startcol;j<startcol+3;j++){
                if(board[i][j]==k) return false;
            }
        }
        return true;

    }
    bool backtrack(vector<vector<char>>& board){
        bool res=false;
        for(int i=0;i<board.size();i++){
            for(int j=0;j<board[i].size();j++){
                if(board[i][j]=='.' ){
                    for(char k='1';k<='9';k++){
                        if(isValid(i,j,k,board)) {
                            board[i][j]=k;
                            if(backtrack(board)){
                                return true;
                            }
                            board[i][j]='.';
                        }
                    }
                    return false;
                } 
            }
        }
        return true;
    }
    void solveSudoku(vector<vector<char>>& board) {
        backtrack(board);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值