2021-05-04Leetcode36-37回溯问题

 36.需要再做

  • 下标才是真正判断依据。
  • 分配或者藏在了下标矩阵里。
  • 初始状态一定要,下标有9这个数字,[9][10]
  • i, j确实是列,[num]这里表示第几个??0-9一共十种情况? 
class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        int row[9][10] = {0};
        int col[9][10] = {0};
        int box[9][10] = {0};   // 判断的时候不搞九宫格,全真的
        for(int i=0; i<board.size(); i++){
            for(int j=0; j<9; j++){
                if(board[i][j]=='.') continue;
                int num = board[i][j]-'0';
                if(row[i][num]) return false;
                if(col[j][num]) return false;
                if(box[j/3+(i/3)*3][num]) return false;
                row[i][num] = 1;
                col[j][num] = 1;
                box[j/3+(i/3)*3][num] = 1;
            }
        }
        return true;
    }
};

//改进后
class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        bool row[9][10] = {false};
        bool col[9][10] = {false};
        bool box[9][10] = {false};   // 判断的时候不搞九宫格,全真的
        for(int i=0; i<board.size(); i++){
            for(int j=0; j<9; j++){
                if(board[i][j]=='.') continue;
                int num = board[i][j]-'0';
                if(row[i][num] || col[j][num] || box[j/3+(i/3)*3][num]) return false;
                row[i][num] = true;
                col[j][num] = true;
                box[j/3+(i/3)*3][num] = true;
            }
        }
        return true;
    }
};

 37.

 

  • 不理解i为何/3之后再*3??
  • 存那些空的位置,要变换一下pair,注意次数只能那么多次,多了不行
  • for循环去1-9,可以就添加数,作为一种情况,什么情况退出??回溯停止条件
  • 必定bool返回
class Solution {
public:
    void solveSudoku(vector<vector<char>>& board) {
        for(int i=0; i<9; i++){
            for(int j=0; j<9; j++){
                if(board[i][j]=='.'){
                    dict.push_back(make_pair(i, j));
                    continue;
                }
                int num = board[i][j]-'1';
                mRow[i][num] = true;
                mCol[j][num] = true;
                mBox[j/3 + i/3*3][num] = true;
            }
        }
        dfs(board, 0);
    }

    bool dfs(vector<vector<char>>& board, int index){
        if(index == dict.size()) return true;//只有这里return一个true
        int r = dict[index].first;
        int c = dict[index].second;
        for(int i=0; i<9; i++){
            if(!mRow[r][i] && !mCol[c][i] && !mBox[c/3 + r/3*3][i]){//全都查无此人?
                board[r][c] = (char)(i+1);      //这里煤气效果
                mRow[r][i] = true;
                mCol[c][i] = true;
                mBox[c/3 + r/3*3][i] = true;
                if(dfs(board, index+1)) return true;    //回溯,发现一个答案就返回
                mRow[r][i] = false;
                mCol[c][i] = false;
                mBox[c/3 + r/3*3][i] = false;
            }
        }
        return false;
    }
private:
    bool mRow[9][9] = {false};
    bool mCol[9][9] = {false};
    bool mBox[9][9] = {false};
    vector<pair<int, int>> dict;
};

 

52.

class Solution {
public:
//递归:遍历每一行,用vector<int> n保存行为i 时的皇后在j列,不能放在斜边:即新的x - y != 已经存在的i - j  || (x + y != i + j)
    vector<int> v;
    unordered_map<int, int> m; //保存用过的列
    int ans = 0;
    void dfs(int row, int n) {
        if (row > n) {
            ans++;
            return;
        }

        for (int j = 1; j <= n; j++) { //row行的列
            if (m.find(j) == m.end()) { 
                int condition = 1;
                for (int k = 1; k < row; k++) { 
                    if (k - v[k] == row - j || k + v[k] == row + j) { //验证对角线是否满足条件
                        condition = 0;
                        break;
                    }
                }
                if (condition == 1) {
                    v[row] = j;
                    m[j]++;
                    dfs(row + 1, n);
                    v[row] = 0; // 回溯
                    m.erase(j); // 删除key为j的map?
                }
            }
        }
    }
    int totalNQueens(int n) {
        v.resize(n + 5);
        dfs(1, n);
        return ans;
    }
    //time : n*n* 不过时间复杂度好像不会算...........
    //space : n  疑惑这个哈希表的空间复杂度?
};

51.

 

class Solution {
public:
vector<vector<string>> ans;


    vector<vector<string>> solveNQueens(int n) {
        vector<string> board(n, string(n, '.')); //可以这样子初始化吗
        vector<bool> column(n, false), ldiag(2*n -1, false), rdiag(2*n-1, false);
        if(n == 0) return ans;
        backtracking(board,column, ldiag, rdiag, 0, n);
        return ans;
    }

    void backtracking(vector<string> &board, vector<bool> &column, vector<bool> &ldiag, vector<bool> &rdiag, int row, int n){
        if(row == n) {
            ans.push_back(board);
            return;
        }
        for(int i=0; i< n; i++){
            if(column[i] || ldiag[n-row+i-1] || rdiag[row+i+1]) continue;
            board[row][i] = 'Q';
            column[i] = ldiag[n-row +i-1]= rdiag[row+i+1] = true;
            backtracking(board, column, ldiag, rdiag, row+1, n);
            board[row][i] = '.';
            column[i] = ldiag[n-row +i-1]= rdiag[row+i+1] = false;
        }
    }
};

401.

hour总共四种,minutes也总共几种,59以内,都是可以直接胖丁的。

string to_string() + ':' + 

 https://leetcode-cn.com/problems/subsets/solution/c-zong-jie-liao-hui-su-wen-ti-lei-xing-dai-ni-gao-/

回溯问题思路?

  • 画出递归树?
  • 找结束条件
  • 找选择列表
  • 路径有无重复,要剪枝吗?
  • 做出选择,
  • 撤销选择

78. 

class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
    vector<vector<int>> subsets(vector<int>& nums) {
        backtracking(nums, 0);
        return ans;
    }

    void backtracking(vector<int>& nums, int start){
        if(start > nums.size()){
            return;
        }
        ans.push_back(path);
        for(int i = start; i<nums.size(); i++){
            path.push_back(nums[i]);
            backtracking(nums, i+1);
            path.pop_back();
        }
    }
};

90.

class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        backtracking(nums, 0);
        return ans;
    }

    void backtracking(vector<int>& nums, int start){
        if(start>nums.size()) return;
        ans.push_back(path);
        for(int i=start; i<nums.size(); i++){
            if(i != start && nums[i]==nums[i-1]) continue;
            path.push_back(nums[i]);
            backtracking(nums, i+1);
            path.pop_back();
        }
    }
};

39.

  • 在何处开始剪枝你不必担心。选择内外都可以。

 

class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        backtracking(candidates, target, 0, 0);
        return ans;
    }

    void backtracking(vector<int>& candidates, int target, int start, int sum){
        if(sum == target){
            ans.push_back(path);
            return;
        }
        if(sum > target) return;
        for(int i=start; i<candidates.size(); i++){
            
            path.push_back(candidates[i]);
            sum += candidates[i];
            backtracking(candidates, target, i, sum);
            path.pop_back();
            sum -= candidates[i];
        }
    }
};

不如上一个
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        backtracking(candidates, target, 0, 0);
        return ans;
    }

    void backtracking(vector<int>& candidates, int target, int start, int sum){
        if(sum == target){
            ans.push_back(path);
            return;
        }
        //if(sum > target) return;
        for(int i=start; i<candidates.size(); i++){
            if(sum > target) continue;
            path.push_back(candidates[i]);
            sum += candidates[i];
            backtracking(candidates, target, i, sum);
            path.pop_back();
            sum -= candidates[i];
        }
    }
};

 

46.

  • 使用find结合vector,可能比再用used进行bool标记效果更好。

public:
vector<vector<int>> ans;
vector<int> path;
    vector<vector<int>> permute(vector<int>& nums) {
        backtracking(nums, 0);
        return ans;
    }

    void backtracking(vector<int>& nums, int index){
        if(index == nums.size()){
            ans.push_back(path);
        }
        for(int i=0; i<nums.size(); i++){
            if(find(path.begin(), path.end(), nums[i])==path.end()){
                path.push_back(nums[i]);
                backtracking(nums, index+1);
                path.pop_back();
            }
        }
    }
};

再次总结:“排列”类型问题和“子集、组合”问题不同在于:“排列”问题使用used数组来标识选择列表,而“子集、组合”问题则使用start参数。另外还需注意两种问题的判重剪枝!

 

47.组合类问题?

未用used,未解出来??

语义为:当i可以选第一个元素之后的元素时(因为如果i=0,即只有一个元素,哪来的重复?有重复即说明起码有两个元素或以上,i>0),然后判断当前元素是否和上一个元素相同?如果相同,再判断上一个元素是否能用?

1223,比如第二个2, used==false,就没用过,现在到第三个==true,用它是可以的,不必continue

上一个元素不能用是??

class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        backtracking(nums, 0);
        return ans;
    }

    void backtracking(vector<int>& nums, int index){
        if(path.size() == nums.size()){
            ans.push_back(path);
            return;
        }
        for(int i =0; i<nums.size(); i++){
            if(find(path.begin(), path.end(), nums[i]) == path.end()){
                if(i!=0 && nums[i]==nums[i-1]){
                    continue;
                }
                path.push_back(nums[i]);
                backtracking(nums, index+1);
                path.pop_back();
            } 
        }
    }
};

正解
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<bool> used;      //但这里used这个数组??里面有重复数字的,如何区分
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        used.resize(nums.size());
        backtracking(nums);
        return ans;
    }

    void backtracking(vector<int>& nums){
        if(path.size() == nums.size()){
            ans.push_back(path);
            return;
        }
        for(int i =0; i<nums.size(); i++){
            if(used[i]==false){
                if(i>0 && nums[i]==nums[i-1] && used[i-1]==false){//相同且上一个false,确实是回溯了,说明已经用过一次了
                    continue;
                }
                path.push_back(nums[i]);
                used[i] = true;
                backtracking(nums);
                path.pop_back();
                used[i] = false;
            } 
        }
    }
};

 剑指 Offer 38. 字符串的排列

 //与前一个字母相等,且前一个字母标记为false,说明前一个字母已经被回溯过,同级,不能再使用当前字母,否则重复

if(i>0 && !used[i] && s.charAt(i)==s.charAt(i-1) && !used[i-1]) continue;

这种出错原因是弄错全局变量了吗?

错误代码:used这个数组初始化失败?
class Solution {
public:
vector<string> res;
vector<bool> used;      //string给你的那个而言的
//string path;
    void backtrack(string s, string path)//used数组
    {
        if(path.size()==s.size())
        {
            res.push_back(path);
            return;
        }
        for(int i=0;i<s.size();i++)
        {
            if(used[i] == false)
            {
                if(i>=1 &&s[i-1]==s[i]&& !used[i-1])//判重剪枝
                    continue;
                path.push_back(s[i]);
                used[i]=true;
                backtrack(s, path);
                used[i]=false;
                path.pop_back();
            }   
        }
    }

    vector<string> permutation(string s) {
        if(s.size()==0)
            return{};
        sort(s.begin(),s.end());
        string path = "";
        vector<bool>used(s.size(), false);
        backtrack(s, path);
        return res;
    }
};

class Solution {
public:
vector<string> res;
//vector<bool> used;      //string给你的那个而言的
//string path;
    void backtrack(string s, string path, vector<bool> used)//used数组
    {
        if(path.size()==s.size())
        {
            res.push_back(path);
            return;
        }
        for(int i=0;i<s.size();i++)
        {
            if(used[i] == false)
            {
                if(i>=1 &&s[i-1]==s[i]&& !used[i-1])//判重剪枝
                    continue;
                path.push_back(s[i]);
                used[i]=true;
                backtrack(s, path, used);
                used[i]=false;
                path.pop_back();
            }   
        }
    }

    vector<string> permutation(string s) {
        if(s.size()==0)
            return{};
        sort(s.begin(),s.end());
        string path = "";
        vector<bool>used(s.size(), false);
        backtrack(s, path, used);
        return res;
    }
};

这种初始化的方式是错误的!!! 

这个vector<bool> 在全局中也定义了 
{vector<bool>used(s.size(), false);}

used.resize(s.size());
这个是对的,而且它默认就是全false,都不设定,
memset你可用

不能使用memset初始化复杂数组!!

 像vector,string这中复杂数据类型不能直接memset, 会破快它的内部结构

vector初始化方式?

则:尽量使用reserve来减少不必要的内存分配次数。

原则:尽量使用empty而不是size()==0 来判断容器是否为空

 

?通过insert初始化?

int a[6] = {6,6,6,6,6,6};
vector<int> b;
//将a的所有元素插入到b中
b.insert(b.begin(), a, a+7);

401.

答案的顺序问题??

vector<string>res;
    unordered_map<int,int> hash={{0,1},{1,2},{2,4},{3,8},{4,1},{5,2},{6,4},{7,8},{8,16},{9,32}};
    void backtrack(int num,int start,pair<int,int>& time)
    {
        if(num==0)
        {
            if(time.first>11||time.second>59)//判断合法性
                return;
            string temp_hour=to_string(time.first);
            string temp_minute=to_string(time.second);
            if(temp_minute.size()==1)//如果minute只有一位要补0
                temp_minute.insert(0,"0");
            res.push_back(temp_hour+":"+temp_minute);//构造格式
            return;
        }
    
        for(int i=start;i<10;i++)
        {
            if(time.first>11||time.second>59)
                continue;
            pair<int,int>store=time;//保存状态
            if(i<4)
                time.first+=hash[i];
            else
                time.second+=hash[i];
            backtrack(num-1,i+1,time);//进入下一层,注意下一层的start是i+1,即从当前灯的下一盏开始
            time=store;//恢复状态
        }
    }
    vector<string> readBinaryWatch(int num) {
        pair<int,int>time(0,0);//初始化时间为0:00
        backtrack(num,0,time);
        return res;
    }


我的代码:
class Solution {
public:
vector<string> ans;
//string path;
// string hour;
// string minute;
pair<int, int> time;
unordered_map<int, int> dict = {{0, 1}, {1, 2}, {2, 4}, {3, 8}, {4, 1}, {5, 2}, {6, 4}, {7, 8}, {8, 16}, {9, 16}, {10, 32}};
    vector<string> readBinaryWatch(int turnedOn) {
        backtracking(turnedOn, 0);
        return ans;
    }

    void backtracking(int turnedOn, int start){
        if(turnedOn == 0){
            if(time.first> 11 || time.second>59){
                return;
            }
            string h = to_string(time.first);
            string m = to_string(time.second);
            if(m.size() == 1) m.insert(0,"0");
            ans.push_back(h+':'+m);
            return;
        }
        for(int i=start; i<10; i++){
            if(time.first> 11 || time.second>59) continue;     //这里我觉得用break更好
            pair<int, int> tmp = time;
            if(i<4){
                time.first += dict[i];
            }else{
                time.second += dict[i];
            }
            backtracking(turnedOn-1, start+1);
            time = tmp;
        }
    }
};

79.

  • 忘了走过的路径要自己标记一下
  •  

  • 为何这种做法出错??标记之后,当回溯的时候,要解除掉。 
  • 在主程序中做了判断,实际不需要这判断。
  •  
class Solution {
public:
bool ans = false;
    bool exist(vector<vector<char>>& board, string word) {
        for(int i=0; i<board.size(); i++){
            for(int j=0; j<board[0].size(); j++){
                if(board[i][j]==word[0]){
                    dfs(board, word, i, j,1);
                }
            }
        }
        return ans;
    }

    void  dfs(vector<vector<char>>& board, string word, int r, int c,int index){
        if(r<0 || r>board.size() || c<0 || c> board[0].size() || board[r][c] != '1') return;
        if(index==word.size()){
            ans = true;
            return;
        }
        char tmp = board[r][c];
        board[r][c] = '1';
        if(board[r-1][c] == word[index]) dfs(board, word, r-1, c, index+1);
        if(board[r+1][c] == word[index]) dfs(board, word, r+1, c, index+1);
        if(board[r][c-1] == word[index]) dfs(board, word, r, c-1, index+1);
        if(board[r][c+1] == word[index]) dfs(board, word, r, c+1, index+1);
        board[r][c] = tmp;
    }
};

c那里是可以整>=size(),注意等于这种勤快。

 212.据说用前缀树???

  • 使用了专用的vector<vector<bool>> used, 而不是修

  • 注意一定要回溯,回归到false去,否则仅26通过。
        used[i][j] = true;
        dfs(board, word, i-1, j, index+1); //+1
        dfs(board, word, i+1, j, index+1);
        dfs(board, word, i, j-1, index+1);
        dfs(board, word, i, j+1, index+1);
        //used[i][j] = false;
class Solution {
public:
vector<string> ans;
string path;
int m, n;
vector<vector<bool>> used;
    vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        m = board.size();
        n = board[0].size();
        vector<bool> tmp(n, false);
        used.resize(m,tmp);
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){//对每个word来一次?
                for(int k = 0; k<words.size(); k++){
                    dfs(board, words[k], i, j, 0);
                }
            }
        }
        return ans;
    }

    void dfs(vector<vector<char>>& board, string& word, int i, int j, int index){
        if(index == word.size()){
            if(find(ans.begin(), ans.end(), word) == ans.end()) ans.push_back(word); //我也不知怎么重复上了
            return;
        }
        if(i<0 || i>=board.size() || j<0 || j>=board[0].size() || used[i][j]==true || board[i][j]!= word[index]){
            return;
        }
        used[i][j] = true;
        dfs(board, word, i-1, j, index+1); //+1
        dfs(board, word, i+1, j, index+1);
        dfs(board, word, i, j-1, index+1);
        dfs(board, word, i, j+1, index+1);
        used[i][j] = false;
    }
};

 

if(r<0 || r>=board.size() || c<0 || c>= board[0].size() || board[r][c] != '1' || board[r][c]!=word[index] || find == true) return;

二维的如何resize一下??? 

vector <vector<int> > v1;
vector<int> temp(4)
v1.resize(5,temp)

313.

  • 注意substr(0,i+1)和substr(i+1)这两个才是连接
  • 对接下来东西也用同样的方法,就是迭代dfs
  • 保证左边全为回文,才往里一个个放,最终的也是回文。

 

class Solution {
public:
vector<vector<string>> ans;
vector<string> path;
    vector<vector<string>> partition(string s) {
        backtracking(s);
        return ans;
    }

    void backtracking(string s){
        if(s == ""){
            ans.push_back(path);
            return;
        }
        for(int i=0; i<s.size(); i++){
            string ls = s.substr(0, i+1);
            string rs = s.substr(i+1);
            if(isHuiWen(ls)){
                path.push_back(ls);
                backtracking(rs);
                path.pop_back();
            }  
        }
    }

    bool isHuiWen(string s){
        int l = 0;
        int r = s.size()-1;
        while(l<r){
            if(s[l] != s[r]) return false;
            l++;
            r--;
        }
        return true;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值